#python #pandas #count #pandas-groupby #frequency
Вопрос:
Я работаю над вариантом использования, в котором у меня есть много наблюдений за многими функциями для каждого идентификатора, и мне нужно подсчитать частоту значений (~5 дискретных значений) для каждого идентификатора в столбце.
У меня есть решение для этого, которое работает для довольно небольшого набора данных (например Я не смог найти чистое решение groupby, так как мне нужно сделать это для многих столбцов сразу.
Примеры данных:
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"feature{i}" 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)
Проблема в том, что melted
есть n*m*k
строки. Мой набор данных содержит >250 функций, >>200 идентификаторов и ~5 тыс. наблюдений на идентификатор, что означает, что >> melted
будет иметь >250 миллионов строк. Это приводит к тому, что моя память в конечном итоге заполняется, и python умирает.
Ожидаемый результат:
feature_freq_id.head(3)
ID | особенность | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|---|
0 | 0 | характеристика 0 | 0.183 | 0.185 | 0.226 | 0.187 | 0.219 |
1 | 0 | характеристика 1 | 0.178 | 0.222 | 0.209 | 0.209 | 0.182 |
2 | 0 | особенность 10 | 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
?
Комментарии:
1. Это работает довольно хорошо и, безусловно, решило проблему краха. Я надеялся, что у pandas есть встроенная функция для этого (именно так я нашел crosstab). Я целенаправленно стараюсь избегать apply() в последнее время из-за некоторых проблем во время выполнения с неудачными вычислениями внутри него раньше. Но я должен был, по крайней мере, попробовать, я думаю.
2. @Nickkon Спасибо за отзыв! Я понимаю ваше
apply
… нежелание. Это удобный инструмент, но он может оказаться неэффективным по сравнению с методами, которые ближе к векторизации (что бы это ни значило). Я бы сказал, что здесь все в порядке, так как он распределяет только родную функцию Pandas по столбцам.