首页 > 解决方案 > 从一列 pandas 创建一个 NxN 矩阵

问题描述

我有数据框,每一行都有一个列表值。

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 类似。

并创建一个 N x N 数据框;像这样:

-  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,))

有一个更好的方法吗?我可以只写一个应用函数而不是进行for循环迭代吗?我可以让它更快吗?

标签: pythonpandasnumpy

解决方案


如果您的数据不是太大,您可以使用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))按行除计数。


推荐阅读