我正在使用 python 中的 Pandas 进行矩阵计算。
我的原始数据是字符串列表的形式(每行都是唯一的)。
id list_of_value
0 ['a','b','c']
1 ['d','b','c']
2 ['a','b','c']
3 ['a','b','c']
Run Code Online (Sandbox Code Playgroud)
我必须用一行和所有其他行计算分数
分数计算算法:
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(0).size
Run Code Online (Sandbox Code Playgroud)
在 id 0 和 id 1,2,3 之间重复步骤 2,3,对于所有 id 都类似。
创建 N * N 矩阵:
- 0 1 2 3
0 1 0.6 1 1
1 0.6 1 1 1
2 1 1 1 1
3 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
目前我正在使用熊猫傻瓜方法来计算分数:
s = pd.get_dummies(df.list_of_value.explode()).sum(level=0)
s.dot(s.T).div(s.sum(1))
Run Code Online (Sandbox Code Playgroud)
但是在矩阵的对角线之后计算有重复,直到对角线的分数计算就足够了。例如:
计算 ID 0 的分数,将只到 ID(row,column) (0,0), score for ID(row,column) (0,1),(0,2),(0,3) 可以从 ID(row,column) (1,0),(2,0),(3,0) 复制。
详细计算:
我需要计算直到对角线,即直到黄色框(矩阵的对角线),白色值已经在绿色阴影区域(参考)中计算出来,我只需要将绿色阴影区域转置为白色。
我怎么能在熊猫中做到这一点?
首先,这里是对您的代码的分析。首先将所有命令分开,然后在您发布它时。
%timeit df.list_of_value.explode()
%timeit pd.get_dummies(s)
%timeit s.sum(level=0)
%timeit s.dot(s.T)
%timeit s.sum(1)
%timeit s2.div(s3)
Run Code Online (Sandbox Code Playgroud)
上述分析返回以下结果:
Explode : 1000 loops, best of 3: 201 µs per loop
Dummies : 1000 loops, best of 3: 697 µs per loop
Sum : 1000 loops, best of 3: 1.36 ms per loop
Dot : 1000 loops, best of 3: 453 µs per loop
Sum2 : 10000 loops, best of 3: 162 µs per loop
Divide : 100 loops, best of 3: 1.81 ms per loop
Run Code Online (Sandbox Code Playgroud)
将您的两行放在一起会导致:
100 loops, best of 3: 5.35 ms per loop
Run Code Online (Sandbox Code Playgroud)
使用一种不同的方法,较少依赖 Pandas 的(有时是昂贵的)功能,通过跳过上三角矩阵和对角线的计算,我创建的代码只需要大约三分之一的时间。
Explode : 1000 loops, best of 3: 201 µs per loop
Dummies : 1000 loops, best of 3: 697 µs per loop
Sum : 1000 loops, best of 3: 1.36 ms per loop
Dot : 1000 loops, best of 3: 453 µs per loop
Sum2 : 10000 loops, best of 3: 162 µs per loop
Divide : 100 loops, best of 3: 1.81 ms per loop
Run Code Online (Sandbox Code Playgroud)
随着df给出
100 loops, best of 3: 5.35 ms per loop
Run Code Online (Sandbox Code Playgroud)
对此代码的分析导致运行时间仅为 1.68 毫秒。
1000 loops, best of 3: 1.68 ms per loop
Run Code Online (Sandbox Code Playgroud)
无需对整个 DataFrame 进行操作,只需选择所需的系列即可大大提高速度。
三种迭代系列中条目的方法已经过测试,所有这些方法在性能方面或多或少是相同的。
import numpy as np
# create a matrix filled with ones (thus the diagonal is already filled with ones)
df2 = np.ones(shape = (len(df), len(df)))
for i in range(len(df)):
d0 = set(df.iloc[i].list_of_value)
d0_len = len(d0)
# the inner loop starts at i+1 because we don't need to calculate the diagonal
for j in range(i + 1, len(df)):
df2[j, i] = len(d0.intersection(df.iloc[j].list_of_value)) / d0_len
# copy the lower triangular matrix to the upper triangular matrix
df2[np.mask_indices(len(df2), np.triu)] = df2.T[np.mask_indices(len(df2), np.triu)]
# create a DataFrame from the numpy array with the column names set to score<id>
df2 = pd.DataFrame(df2, columns = [f"score{i}" for i in range(len(df))])
Run Code Online (Sandbox Code Playgroud)
熊猫有很多陷阱。例如,总是通过df.iloc[0]而不是访问 DataFrame 或 Series 的行df[0]。两者都有效,但df.iloc[0]速度要快得多。
第一个矩阵的时序有 4 个元素,每个元素的列表大小为 3,因此速度提高了大约 3 倍。
1000 loops, best of 3: 443 µs per loop
Run Code Online (Sandbox Code Playgroud)
当使用更大的数据集时,我得到了更好的结果,加速比超过 11:
# operating on the DataFrame
10 loop, best of 3: 565 ms per loop
# operating on the Series
10 loops, best of 3: 47.7 ms per loop
Run Code Online (Sandbox Code Playgroud)
当根本不使用 Pandas 时(在计算过程中),你会得到另一个显着的加速。因此,您只需要将要操作的列转换为列表。
df = pd.DataFrame(
[[['a','b','c']],
[['d','b','c']],
[['a','b','c']],
[['a','b','c']]],
columns = ["list_of_value"])
Run Code Online (Sandbox Code Playgroud)
在问题中提供的数据上,与第一次更新相比,我们只看到稍微好一点的结果。
1000 loops, best of 3: 363 µs per loop
Run Code Online (Sandbox Code Playgroud)
但是当使用更大的数据(100 行,列表大小为 15)时,优势变得明显:
100 loops, best of 3: 5.26 ms per loop
Run Code Online (Sandbox Code Playgroud)
这是所有建议方法的比较:
+----------+-----------------------------------------+
| | Using the Dataset from the question |
+----------+-----------------------------------------+
| Question | 100 loops, best of 3: 4.63 ms per loop |
+----------+-----------------------------------------+
| Answer | 1000 loops, best of 3: 1.59 ms per loop |
+----------+-----------------------------------------+
| Update 1 | 1000 loops, best of 3: 447 µs per loop |
+----------+-----------------------------------------+
| Update 2 | 1000 loops, best of 3: 362 µs per loop |
+----------+-----------------------------------------+
Run Code Online (Sandbox Code Playgroud)