Dask.dataframe или альтернатива: масштабируемый способ отбрасывания строк низкочастотных элементов
Я ищу способ удалить строки из кадра данных, которые содержат низкочастотные элементы. Я адаптировал следующий фрагмент из этого поста:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(0, high=9, size=(100,2)),
columns = ['A', 'B'])
threshold = 10 # Anything that occurs less than this will be removed.
value_counts = df.stack().value_counts() # Entire DataFrame
to_remove = value_counts[value_counts <= threshold].index
df.replace(to_remove, np.nan, inplace=True)
Проблема в том, что этот код не масштабируется, кажется.
Линия to_remove = value_counts[value_counts <= threshold].index
в настоящее время работает в течение нескольких часов для моих данных (2 ГБ сжатый HDFStore). Поэтому мне нужно лучшее решение. Идеально вне ядра. Я подозреваю dask.dataframe
подходит, но я не могу выразить вышеупомянутый код в терминах dask. Ключевые функции stack
а также replace
отсутствуют в dask.dataframe
,
Я попытался следующее (работает в обычных пандах), чтобы обойти отсутствие этих двух функций:
value_countss = [df[col].value_counts() for col in df.columns]
infrequent_itemss = [value_counts[value_counts < 3] for value_counts in value_countss]
rows_to_drop = set(i for indices in [df.loc[df[col].isin(infrequent_items.keys())].index.values for col, infrequent_items in zip(df.columns, infrequent_itemss)] for i in indices)
df.drop(rows_to_drop)
Это на самом деле не работает с Dask, хотя. Это ошибки при infrequent_items.keys()
,
Даже если это сработало, учитывая, что это противоположность элегантности, я подозреваю, что должен быть лучший способ.
Можете ли вы предложить что-то?
2 ответа
Следующий код, который включает в себя улучшения Эвана, решает мою проблему:
unique, counts = np.unique(df.values.ravel(), return_counts=True)
d = dict(zip(unique, counts))
to_remove = {k for k, v in d.items() if v < threshold}
mask = df.isin(to_remove)
column_mask = (~mask).all(axis=1)
df = df[column_mask]
демо:
def filter_low_frequency(df, threshold=4):
unique, counts = np.unique(df.values.ravel(), return_counts=True)
d = dict(zip(unique, counts))
to_remove = {k for k, v in d.items() if v < threshold}
mask = df.isin(to_remove)
column_mask = (~mask).all(axis=1)
df = df[column_mask]
return df
df = pd.DataFrame(np.random.randint(0, high=20, size=(10,10)))
print(df)
print(df.stack().value_counts())
df = filter_low_frequency(df)
print(df)
0 1 2 3 4 5 6 7 8 9
0 3 17 11 13 8 8 15 14 7 8
1 2 14 11 3 16 10 19 19 14 4
2 8 13 13 17 3 13 17 18 5 18
3 7 8 14 9 15 12 0 15 2 19
4 6 12 13 11 16 6 19 16 2 17
5 2 1 2 17 1 3 12 10 2 16
6 0 19 9 4 15 3 3 3 4 0
7 18 8 15 9 1 18 15 17 9 0
8 17 15 9 11 13 9 11 4 19 8
9 13 6 7 8 8 10 0 3 16 13
8 9
3 8
13 8
17 7
15 7
19 6
2 6
9 6
11 5
16 5
0 5
18 4
4 4
14 4
10 3
12 3
7 3
6 3
1 3
5 1
dtype: int64
0 1 2 3 4 5 6 7 8 9
6 0 19 9 4 15 3 3 3 4 0
8 17 15 9 11 13 9 11 4 19 8
Не уверен, поможет ли это вам, но он слишком велик для комментария:
df = pd.DataFrame(np.random.randint(0, high=20, size=(30,2)), columns = ['A', 'B'])
unique, counts = np.unique(df.values.ravel(), return_counts=True)
d = dict(zip(unique, counts))
threshold = 10
to_remove = [k for k, v in d.items() if v < threshold]
df.replace(to_remove, np.nan, inplace=True)
Увидеть:
Как посчитать вхождение определенного элемента в ndarray в Python?
как посчитать вхождение каждого уникального значения в панды
Задача с игрушкой показала 40-кратное ускорение от 400 до 10 в указанном вами шаге.