numpy:如何表达多人关系?

2024-06-01 06:17:33 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有以下逻辑关系:

# ANIMALS
cat = [hobbes, tigger, garfield]
dog = [lassie]
frog = [kermit, hypnotoad]

# HABITATS
tree = [cat, frog]
river = [dog, frog, turtle]
house = [cat, dog]

一个集合中永远不会有重复项。我想把它们配对成(animal, habitat)

(hobbes, tree)
(garfield, tree)
(tigger, tree)
(kermit, tree)
(hypnotoad, tree)
(lassie, river)
(kermit, river)
(hypnotoad, river)
(hobbes, house)
(garfield, house)
(tigger, house)
(lassie, house)

如果这是SQL,它将类似于

CREATE TABLE animal (animal_name, animal_type);
CREATE TABLE habitat_to_animal (habitat_type, animal_type);

SELECT
    animal.animal_name,
    habitat_to_animal.habitat_type
FROM animal
JOIN habitat_to_animal
    ON animal.animal_type = habitat_to_animal.animal_type

它们存储在numpy数组中(实际数组是numpy.int32),如下所示:

# animals are sorted by animal_kind
animal_kind = ['cat', 'cat', 'cat', 'dog', 'frog', 'frog']
animal_name = ['hobbes', 'tigger', 'garfield', 'lassie', 'kermit', 'hypnotoad']

# habitats are sorted by habitat_type
habitat_type = ['tree', 'tree', 'river', 'river', 'river', 'house', 'house']
habitat_animal = ['cat', 'frog', 'dog', 'frog', 'turtle', 'cat', 'fish']

如果数组非常大(每个都有数百万个条目),那么在numpy中执行此操作的最快方法是什么

编辑:这只能是numpy(没有熊猫或其他LIB)。就数据基数而言,假设两组都是约1000万只“动物”和约1亿只“栖息地”,实际输出2亿对(大多数栖息地将是空的,一些栖息地将有5-10只动物)。这意味着生成完整的交叉积和过滤是不可能的。就像任何循环一样;数据太大了

实际数组是整数ID,与动物或栖息地无关,但这样更容易阅读=)


Tags: treetypecathousedogtiggeranimalkermit
2条回答
In [113]: animal_kind = ['cat', 'cat', 'cat', 'dog', 'frog', 'frog']
     ...: animal_name = ['hobbes', 'tigger', 'garfield', 'lassie', 'kermit', 'hy
     ...: pnotoad']
     ...: 
     ...: habitat_type = ['tree', 'tree', 'river', 'river', 'river', 'house', 'h
     ...: ouse', 'house']
     ...: habitat_animal = ['cat', 'frog', 'dog', 'frog', 'turtle', 'cat', 'fish
     ...: ', 'dog']
     ...: 

将它们转换为阵列:

In [115]: kind,name,type,animal=[np.array(x) for x in (animal_kind,animal_name,h
     ...: abitat_type, habitat_animal)]

做一张与这一类相匹配的桌子。这个可能很大

In [116]: kind[:,None]==animal
Out[116]: 
array([[ True, False, False, False, False,  True, False, False],
       [ True, False, False, False, False,  True, False, False],
       [ True, False, False, False, False,  True, False, False],
       [False, False,  True, False, False, False, False,  True],
       [False,  True, False,  True, False, False, False, False],
       [False,  True, False,  True, False, False, False, False]])

查找匹配项(True):

In [117]: np.nonzero(_)
Out[117]: 
(array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5]),
 array([0, 5, 0, 5, 0, 5, 2, 7, 1, 3, 1, 3]))

使用这些索引从其他数组中选择项目:

In [118]: np.stack([name[_[0]],type[_[1]]])
Out[118]: 
array([['hobbes', 'hobbes', 'tigger', 'tigger', 'garfield', 'garfield',
        'lassie', 'lassie', 'kermit', 'kermit', 'hypnotoad', 'hypnotoad'],
       ['tree', 'house', 'tree', 'house', 'tree', 'house', 'river',
        'house', 'tree', 'river', 'tree', 'river']], dtype='<U9')

该配对可以转换为:

In [119]: _.T
Out[119]: 
array([['hobbes', 'tree'],
       ['hobbes', 'house'],
       ['tigger', 'tree'],
       ['tigger', 'house'],
       ['garfield', 'tree'],
       ['garfield', 'house'],
       ['lassie', 'river'],
       ['lassie', 'house'],
       ['kermit', 'tree'],
       ['kermit', 'river'],
       ['hypnotoad', 'tree'],
       ['hypnotoad', 'river']], dtype='<U9')

这里是按名称分组的。要按栖息地分组,请使用[116]的转置

In [123]: I,J = np.nonzero(_116.T)
In [124]: np.stack([name[J],type[I]], axis=1)
Out[124]: 
array([['hobbes', 'tree'],
       ['tigger', 'tree'],
       ['garfield', 'tree'],
       ['kermit', 'tree'],
       ['hypnotoad', 'tree'],
       ['lassie', 'river'],
       ['kermit', 'river'],
       ['hypnotoad', 'river'],
       ['hobbes', 'house'],
       ['tigger', 'house'],
       ['garfield', 'house'],
       ['lassie', 'house']], dtype='<U9')

迭代的

迭代列表解决方案是:

In [188]: alist = []
     ...: for t,a in zip(habitat_type, habitat_animal):
     ...:     for k,n in zip(animal_kind,animal_name):
     ...:         if k==a:
     ...:             alist.append([n,t,k])
     ...: 
     ...: 
In [189]: alist
Out[189]: 
[['hobbes', 'tree', 'cat'],
 ['tigger', 'tree', 'cat'],
 ['garfield', 'tree', 'cat'],
 ['kermit', 'tree', 'frog'],
 ['hypnotoad', 'tree', 'frog'],
 ['lassie', 'river', 'dog'],
 ['kermit', 'river', 'frog'],
 ['hypnotoad', 'river', 'frog'],
 ['hobbes', 'house', 'cat'],
 ['tigger', 'house', 'cat'],
 ['garfield', 'house', 'cat'],
 ['lassie', 'house', 'dog']]

由于animal_kind是排序的,我们应该能够通过只迭代“下一个”类别组来缩短内部循环。这是一个簿记细节,我将留给你(除非我感到无聊)

如果您想要SQL这样的选择功能,我建议您考虑使用Pandas来处理数据。它可以愉快地存储Numpy数组,但您有非常灵活的筛选选项,可以通过多种不同的方式对数据进行切片

对于您所要求的,我不知道您是否需要做更多的事情,而不仅仅是创建数据帧并对其进行迭代。下面是一个试图与您的SQL示例保持一致的示例:

import pandas as pd

animal_kind = ['cat', 'cat', 'cat', 'dog', 'frog', 'frog']
animal_name = ['hobbes', 'tigger', 'garfield', 'lassie', 'kermit', 'hypnotoad']

habitat_type = ['tree', 'tree', 'river', 'river', 'river', 'house', 'house', 'house']
habitat_animal = ['cat', 'frog', 'dog', 'frog', 'turtle', 'cat', 'fish', 'dog']

df_animal = pd.DataFrame(data=animal_kind, columns=["animal"], index=animal_name)
habitat_to_animal = pd.DataFrame()
habitat_to_animal["animal"] = habitat_animal
habitat_to_animal["habitat"] = habitat_type

for i in range(len(habitat_to_animal)):
    animal, habitat = habitat_to_animal.loc[i]
    names = df_animal[df_animal.animal == animal].index
    for name in names:
        print(f"({name},{habitat})")

这将按要求生成您的列表。我认为它可以很好地扩展,但是有3-4种不同的方法来迭代Pandas中的行,最快的方法取决于数据和您对它所做的操作

相关问题 更多 >