使用numpy/pandas创建两个列的联合命中次数矩阵

2 投票
2 回答
779 浏览
提问于 2025-04-17 21:40

我有两列很大的数据(大约150万个值)。它们的结构是:

     col1 = [2,2,1,4,5,4,3,4,4,4,5,2,3,1,1 ..] etc.,
     col2 = [1,1,8,8,3,5,6,7,2,3,10.........] etc.,

第一列的值在1到5之间,第二列的值在1到10之间。我想制作一个联合计数矩阵,叫做CountsMAT。

     counts of [(1,2),(1,3),...(1,10),
                (2,1), (2,2),....(2,10),
                (3,1),......,(3,3)...(3,10),
                ...........................
                (5,1),(5,2),...(5,5).....(5,10)] are required ie.,

     a CountsMAT of size (5,10) 
     max(col1) = 5; max(col2) = 10 ;  

我用defaultdict和for循环实现了一个版本,但这样做花了不少时间。我相信用Pandas直接处理会更有效率,我希望能得到一个使用numpy/pandas的最佳实现。很多类似的问题都提到了Pandas的'groupby',但我对这个不太熟悉。

用我基于字典的实现来得到矩阵格式的输出也比较麻烦,不过我觉得用Pandas/numpy会简单一些。谢谢!

2 个回答

1

numpy_indexed这个包(声明:我是它的作者)有一个函数可以高效地解决这个问题:

import numpy_indexed as npi
print(npi.count_table(col1, col2))
5

这可能可以实现(使用 numpy.histogram2d):

import numpy as np

col1 = np.random.random_integers(1, 5, 100)
col2 = np.random.random_integers(1, 10, 100)
bin1 = np.arange(0.5, 6.5, 1)
bin2 = np.arange(0.5, 11.5, 1)

mat = np.histogram2d(col1, col2, bins=[bin1, bin2])[0]
print mat.shape
print mat

这会产生

(5, 10)
array([[ 4.,  2.,  0.,  6.,  2.,  2.,  1.,  2.,  1.,  2.],
       [ 3.,  3.,  3.,  0.,  3.,  0.,  1.,  4.,  1.,  0.],
       [ 4.,  2.,  1.,  1.,  3.,  2.,  5.,  2.,  2.,  2.],
       [ 1.,  1.,  3.,  2.,  3.,  1.,  4.,  4.,  0.,  0.],
       [ 0.,  2.,  1.,  4.,  3.,  1.,  3.,  2.,  0.,  1.]])

我还没有测试过长度超过几千的列表,但我觉得对于几百万个值来说应该没问题。


编辑:

嗨 @nahsivar。我对 pandas 不是很熟悉(但我应该多了解一下),所以我花了几分钟试了一下。这里有几种方法可以实现你想要的(我想):

#instantiate dataframe
import pandas as pd
from random import choice
x_choice = 'ABCDE'
y_choice = 'ABCDEFGHIJ'
x, y = [], []
for i in range(100):
    x[i] = choice(x_choice)
    y[i] = choice(y_choice)

df = pd.DataFrame(data={'col1': x, 'col2': y})

# 1
df.pivot_table(rows='col1', cols='col2', aggfunc=len) 
# Use fill_value=0 to replace the NaNs with 0
# Output:
col2   A  B   C   D   E  F  G  H  I   J
col1                                   
A    NaN  1   3   1   2  2  2  1  4   2
B      1  1 NaN   3   5  1  2  3  1 NaN
C      4  1   2 NaN NaN  4  3  2  1   2
D      2  2   2   1   1  3  3  4  4   2
E      1  1   1 NaN   4  2  6  3  2   2

# 2
df.groupby('col2')['col1'].value_counts().unstack(level=0)
# Output:
col2   A  B   C   D   E  F  G  H  I   J
A    NaN  1   3   1   2  2  2  1  4   2
B      1  1 NaN   3   5  1  2  3  1 NaN
C      4  1   2 NaN NaN  4  3  2  1   2
D      2  2   2   1   1  3  3  4  4   2
E      1  1   1 NaN   4  2  6  3  2   2

# 3
pd.crosstab(df.col1, df.col2)
# Output:
col2  A  B  C  D  E  F  G  H  I  J
col1                              
A     0  1  3  1  2  2  2  1  4  2
B     1  1  0  3  5  1  2  3  1  0
C     4  1  2  0  0  4  3  2  1  2
D     2  2  2  1  1  3  3  4  4  2
E     1  1  1  0  4  2  6  3  2  2

撰写回答