从一列创建NxN矩阵

2024-06-25 06:20:46 发布

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

我有一个dataframe,每一行都有一个列表值

id     list_of_value
0      ['a','b','c']
1      ['d','b','c']
2      ['a','b','c']
3      ['a','b','c']

我必须计算一行的分数,然后对照所有其他行

例如:

Step 1: Take value of id 0: ['a','b','c'],
Step 2: find the intersection between id 0 and id 1 , 
        resultant = ['b','c']
Step 3: Score Calculation => resultant.size / id.size

在id 0和id 1,2,3之间重复步骤2,3,对所有id重复相同操作。

并创建一个nxn数据帧;例如:

-  0  1    2  3
0  1  0.6  1  1
1  1  1    1  1 
2  1  1    1  1
3  1  1    1  1

现在我的代码只有一个for循环:

def scoreCalc(x,queryTData):
    #mathematical calculation
    commonTData = np.intersect1d(np.array(x),queryTData)
    return commonTData.size/queryTData.size

ids = list(df['feed_id'])
dfSim = pd.DataFrame()

for indexQFID in range(len(ids)):
    queryTData = np.array(df.loc[df['id'] == ids[indexQFID]]['list_of_value'].values.tolist())

    dfSim[segmentDfFeedIds[indexQFID]] = segmentDf['list_of_value'].apply(scoreCalc,args=(queryTData,))

有更好的方法吗?我可以只编写一个apply函数,而不是执行for循环迭代吗。 我能快点吗


Tags: ofididsdfforsizevaluestep
3条回答

如果数据不太大,可以使用get_dummies对值进行编码并执行矩阵乘法:

s = pd.get_dummies(df.list_of_value.explode()).sum(level=0)
s.dot(s.T).div(s.sum(1))

输出:

          0         1         2         3
0  1.000000  0.666667  1.000000  1.000000
1  0.666667  1.000000  0.666667  0.666667
2  1.000000  0.666667  1.000000  1.000000
3  1.000000  0.666667  1.000000  1.000000

更新:下面是对代码的简短解释。其主要思想是将给定列表转换为一个热编码列表:

   a  b  c  d
0  1  1  1  0
1  0  1  1  1
2  1  1  1  0
3  1  1  1  0

一旦我们有了它,两行的交集的大小,比如说,01就是它们的点积,因为一个字符属于这两行,当且仅当它在这两行中都由1表示

记住这一点,首先使用

df.list_of_value.explode()

将每个单元格转换为一个系列并连接所有这些系列。输出:

0    a
0    b
0    c
1    d
1    b
1    c
2    a
2    b
2    c
3    a
3    b
3    c
Name: list_of_value, dtype: object

现在,我们在该系列上使用pd.get_dummies将其转换为一个热编码数据帧:

   a  b  c  d
0  1  0  0  0
0  0  1  0  0
0  0  0  1  0
1  0  0  0  1
1  0  1  0  0
1  0  0  1  0
2  1  0  0  0
2  0  1  0  0
2  0  0  1  0
3  1  0  0  0
3  0  1  0  0
3  0  0  1  0

如您所见,每个值都有自己的行。由于我们希望将属于同一原始行的数据合并到一行中,因此我们可以通过原始索引对它们求和。因此

s = pd.get_dummies(df.list_of_value.explode()).sum(level=0)

给出所需的二进制编码数据帧。下一行

s.dot(s.T).div(s.sum(1))

正如您的逻辑:s.dot(s.T)按行计算点积,然后.div(s.sum(1))按行除以计数

试试这个

range_of_ids = range(len(ids))

def score_calculation(s_id1,s_id2):
    s1 = set(list(df.loc[df['id'] == ids[s_id1]]['list_of_value'])[0])
    s2 = set(list(df.loc[df['id'] == ids[s_id2]]['list_of_value'])[0])
    # Resultant calculation s1&s2
    return round(len(s1&s2)/len(s1) , 2)


dic = {indexQFID:  [score_calculation(indexQFID,ind) for ind in range_of_ids] for indexQFID in range_of_ids}
dfSim = pd.DataFrame(dic)
print(dfSim)

输出

     0        1      2       3
0   1.00    0.67    1.00    1.00
1   0.67    1.00    0.67    0.67
2   1.00    0.67    1.00    1.00
3   1.00    0.67    1.00    1.00

您也可以按如下方式进行操作

dic = {indexQFID:  [round(len(set(s1)&set(s2))/len(s1) , 2) for s2 in df['list_of_value']] for indexQFID,s1 in zip(df['id'],df['list_of_value']) }
dfSim = pd.DataFrame(dic)
print(dfSim)

对集合s_list的列表使用嵌套列表理解。在列表理解中,使用intersection操作检查重叠并获得每个结果的长度。最后,构造数据帧并将其除以df.list_of_value中每个列表的长度

s_list =  df.list_of_value.map(set)
overlap = [[len(s1 & s) for s1 in s_list] for s in s_list]

df_final = pd.DataFrame(overlap) / df.list_of_value.str.len().to_numpy()[:,None]

Out[76]:
          0         1         2         3
0  1.000000  0.666667  1.000000  1.000000
1  0.666667  1.000000  0.666667  0.666667
2  1.000000  0.666667  1.000000  1.000000
3  1.000000  0.666667  1.000000  1.000000

如果每个列表中都有重复的值,则应使用collections.Counter而不是set。我将样本数据id=0更改为['a','a','c'],将id=1更改为['d','b','a']

sample df:
id     list_of_value
0      ['a','a','c'] #changed
1      ['d','b','a'] #changed
2      ['a','b','c']
3      ['a','b','c']

from collections import Counter

c_list =  df.list_of_value.map(Counter)
c_overlap = [[sum((c1 & c).values()) for c1 in c_list] for c in c_list]

df_final = pd.DataFrame(c_overlap) / df.list_of_value.str.len().to_numpy()[:,None]


 Out[208]:
          0         1         2         3
0  1.000000  0.333333  0.666667  0.666667
1  0.333333  1.000000  0.666667  0.666667
2  0.666667  0.666667  1.000000  1.000000
3  0.666667  0.666667  1.000000  1.000000

相关问题 更多 >