Pandas,值的组合并检查是否有任何组合在列表中

x9ybnkn6  于 2023-03-28  发布在  其他
关注(0)|答案(2)|浏览(121)

我有一个df1,其中包含一些item_id,以及每个项目的一些值(称为“节点”):

df1 = pd.DataFrame({'item_id':['1','1','1','2','2','2','3','3'],'nodes':['a','b','c','d','a','e','f','g']})

和一个df2,这是一个“向量”列表,其中每行是一个节点元组(可以在df1中,但其中一些不是):

df2=pd.DataFrame({'vectors':[('a','b'),('b','c'),('d','f'),('e','b')]})

我需要计算df1中不同的item_id的数量,这些item_iddf2中至少有一个向量,因为向量可以从该项目的所有可能的节点组合中构造出来。
例如,item_id = 1具有节点[a,b,c],因此可以形成这些向量:[(a,b),(a,c),(b,a),(b,c),(c,a),(c,b)]。由于向量(a,b)(b,c)存在于df2中,那么我应该计数item_id = 1。但是,我不应该计数item_id = 2,因为从所有可以由其节点组合形成的向量中,没有一个在df2中。
我不知道如何才能做到这一点。我可以获得所有可能的节点组合列表,以形成df1中第一个item_id的不同向量,使用:

from itertools import product
nodes_fa=df1[df1.item_id=="1"].nodes.to_list()
vectors_fa = pd.DataFrame(product(nodes_fa,nodes_fa),columns=['u','v'],dtype='str')
vectors_fa['vector'] = vectors_fa[["u", "v"]].agg(tuple, axis=1)
vectors_fa = vectors_fa[['vector']]
display(vectors_fa)

但是我不知道如何将其扩展到所有的item_id,也不知道如何检查这个列表中的任何值是否在循环中的df2中。
任何帮助都将不胜感激。

ha5z0ras

ha5z0ras1#

您可以使用itertools.combinationsgroupby.apply,并在set/frozenset的帮助下:
要考虑无向边(('a', 'b') == ('b', 'a')):

from itertools import combinations

S = set(frozenset(x) for x in df2['vectors'])

out = (
 df1.groupby('item_id')['nodes']
    .apply(lambda g: any(frozenset(t) in S for t in combinations(g, r=2)))
    .sum()
)

对于有向边(('a', 'b')!= ('b', 'a')):

from itertools import combinations

S = set(df2['vectors'])

out = (
 df1.groupby('item_id')['nodes']
    .apply(lambda g: any(t in S for t in combinations(g, r=2)))
    .sum()
)

输出:1
使用pandas函数的替代方案(可能效率较低):

s = (df1.groupby('item_id')['nodes']
        .agg(lambda g: list(combinations(g, r=2)))
        .explode()
     )

out = s.isin(df2['vectors']).groupby(level=0).any().sum()
5vf7fwbs

5vf7fwbs2#

我想提出一个使用合并而不是依赖于apply的解决方案,这取决于存在多少个item_id值,以及每个item_id有多少行,它可能也会更好地提高性能。

merged_df1 = pd.merge(df1, df1, left_on='item_id', right_on='item_id')
#eliminates the (x,y) pairs where x == y. Remove if this is not the intended behavior
merged_df1 =  merged_df1[merged_df1['nodes_x'] != merged_df1['nodes_y']]

#splitting tuples of df2 into 2 columns
df2[['node_1', 'node_2']] = pd.DataFrame(df2.vectors.tolist())
valid_id = pd.merge(merged_df1, df2, 
                    left_on=['nodes_x','nodes_y'], 
                    right_on=['node_1', 'node_2']
                    ).item_id.unique()
out = len(valid_id)

相关问题