问题
我正在使用Python 3.7.7上的Pandas。我想计算变量x
的分类值在另一个变量y
的值分组下的互信息。我的数据看起来像下面的表格:
+-----+-----+
| x | y |
+-----+-----+
| x_1 | y_1 |
| x_2 | y_1 |
| x_3 | y_1 |
| x_1 | y_2 |
| x_2 | y_2 |
| x_4 | y_3 |
| x_6 | y_3 |
| x_9 | y_3 |
| x_1 | y_4 |
| ... | ... |
+-----+-----+
我希望有一个数据结构(pandas MultiIndex系列/数据框或numpy矩阵或其他合适的东西),它可以存储给定特定y_k
值的x_i
和x_j
对的共现次数。实际上,这将非常好,例如,可以轻松计算PMI:
+-----+-----+--------+-------+
| x_i | x_j | cooc | pmi |
+-----+-----+--------+-------+
| x_1 | x_2 | | |
| x_1 | x_3 | | |
| x_1 | x_4 | | |
| x_1 | x_5 | | |
| ... | ... | ... | ... |
+-----+-----+--------+-------+
有没有适合的内存高效方式?
附注:我正在使用相当大的数据(40k个不同的x
值和8k个不同的y
值,总共有300k个(x
,y
)条目,因此希望能够使用内存友好且经过优化的方法(也许依赖于第三方库,如Dask)
更新
非优化解决方案
我想出了一种使用pd.crosstab的解决方案。这里提供一个小例子:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(0,100,size=(100, 2)), columns=list('xy'))
"""
df:
+-----+-----+
| x | y |
+-----+-----+
| 4 | 99 |
| 1 | 39 |
| 39 | 56 |
| .. | .. |
| 59 | 20 |
| 82 | 57 |
+-----+-----+
100 rows × 2 columns
"""
# Compute cross tabulation:
crosstab = pd.crosstab(df["x"], df["y"])
"""
crosstab:
+------+-----+-----+-----+-----+
| y | 0 | 2 | 3 | ... |
| x +-----+-----+-----+-----+
| 1 | 0 | 0 | 0 | ... |
| 2 | 0 | 0 | 0 | ... |
| ... | ... | ... | ... | ... |
+------+-----+-----+-----+-----+
62 rows × 69 columns
"""
# Initialize a pandas MultiIndex Series storing PMI values
import itertools
x_pairs = list(itertools.combinations(crosstab.index, 2))
pmi = pd.Series(0, index = pd.MultiIndex.from_tuples(x_pairs))
"""
pmi:
+-------------+-----+
| index | val |
+------+------| |
| x_i | x_j | |
+------+------+-----+
| 1 | 2 | 0 |
| | 4 | 0 |
| ... | ... | ... |
| 95 | 98 | 0 |
| | 99 | 0 |
| 96 | 98 | 0 |
+------+------+-----+
Length: 1891, dtype: int64
"""
然后,我用来填充Series的循环结构如下:
for x1, x2 in x_pairs:
pmi.loc[x1, x2] = crosstab.loc[[x1, x2]].min().sum() / (crosstab.loc[x1].sum() * crosstab.loc[x2].sum())
这不是一个可选的解决方案,即使在小型用例中性能也很差。
x
的组合会被观察到,使用稀疏矩阵表示是否公平? - SultanOrazbayev