更有效的方法:按变量分组的许多列的 value_counts (in %)

Posted

技术标签:

【中文标题】更有效的方法:按变量分组的许多列的 value_counts (in %)【英文标题】:More efficient way for: value_counts (in %) for many columns grouped by a variable 【发布时间】:2021-10-31 09:29:29 【问题描述】:

我正在研究一个用例,其中我对每个 id 的许多特征有很多观察,我需要计算每列每个 id 的值的频率(约 5 个离散值)。

我有一个解决方案,它适用于相当小的数据集(例如

样本数据:

import pandas as pd
import numpy as np

n = 100     # Number of features
m = 100     # Number of classes
k = 10000   # number of occurrences per class

possible_values = [1, 2, 3, 4, 5]

df = []
for i in range(m):
    for j in range(k):
        df.append( np.append(i, np.random.choice(possible_values, n)) )

features = [f"featurei" for i in range(n)]
df = pd.DataFrame(df, columns=(["id"] + features))

不用 groupby 就很简单了:

df[features].apply(pd.value_counts).T / df.shape[0]

我的方法

melted = df.melt(id_vars="id", var_name='feature', value_name='value')
feature_freq_id = pd.crosstab(index=[melted.id, melted.feature], columns=melted.value).reset_index()
feature_freq_id[possible_values] = feature_freq_id[possible_values].div(feature_freq_id[possible_values].sum(axis=1), axis=0)

问题是meltedn*m*k 行。我的数据集有 >250 个特征、>200 个 id 和每个 id 约 5k 观察值,这意味着 melted 将有 >2.5 亿行。这导致我的内存最终被填满,python 死了。

预期结果:

feature_freq_id.head(3)
id feature 1 2 3 4 5
0 0 feature0 0.183 0.185 0.226 0.187 0.219
1 0 feature1 0.178 0.222 0.209 0.209 0.182
2 0 feature10 0.215 0.213 0.175 0.196 0.201

【问题讨论】:

【参考方案1】:

只是一个想法:结合您的“简单”方法使用groupby 而不是id

def fractions(sdf):
    return sdf.apply(pd.value_counts, normalize=True).fillna(0.).T
    
result = df.groupby("id")[features].apply(fractions)
result.index.set_names("feature", level=1, inplace=True)

这应该避免内存melt-down?

【讨论】:

效果很好,确实解决了崩溃问题。我希望 pandas 有一个内置功能(这就是我找到 crossstab 的方式)。我最近故意尝试避免 apply() ,因为之前在其中遇到了一些运行时问题以及不幸的计算。但我想我至少应该尝试过。 @Nickkon 感谢您的反馈!我理解你的apply-reluctance。它是一个方便的工具,但与更接近矢量化 的方法(无论这意味着什么)相比,它可能表现不佳。我想说这里没问题,因为它只在列上分发原生 Pandas 函数。

以上是关于更有效的方法:按变量分组的许多列的 value_counts (in %)的主要内容,如果未能解决你的问题,请参考以下文章

按“内部”分区键进行有效分组

是否有更快(更简洁)的方法来为 JDBC 中的 PreparedStatements 设置许多参数?

SQL:按结果分组的有效方法,包括所有表列

如何根据分组变量计算所有列的总和并删除 NA

Apache Spark SQL 按范围分组数据

以日历季度为列分组