按条件从列表的数据框列中计算和删除元素
Posted
技术标签:
【中文标题】按条件从列表的数据框列中计算和删除元素【英文标题】:Count and remove elements by conditions from dataframe columns of lists 【发布时间】:2018-02-21 20:02:12 【问题描述】:假设我有一个 Pandas df
col_name
1 [16, 4, 30]
2 [5, 1, 2]
3 [4, 5, 52, 888]
4 [1, 2, 4]
5 [5, 99, 4, 75, 1, 2]
我想删除整列中出现less than x
次的所有元素,例如我们取x = 3
这意味着我想让结果看起来像:
col_name
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
结果df基本上去掉了数字16、30、52、888、99和75,因为它在列中出现的次数不到3次。
我尝试使用来自collections
的Counter
,但没有成功。
如果你能给我任何提示,我真的很感激。提前致谢。
【问题讨论】:
【参考方案1】:选项 1 有点普通的香草方法
s = pd.Series((i, j): x for (i, r) in df.col_name.items() for j, x in enumerate(r))
f, u = pd.factorize(s.values)
s[(np.bincount(f) >= 3)[f]].groupby(level=0).apply(list).to_frame('col_name')
col_name
0 [4]
1 [5, 1, 2]
2 [4, 5]
3 [1, 2, 4]
4 [5, 4, 1, 2]
选项 2 过于复杂的方法
lens = df.col_name.str.len().values
splits = lens.cumsum()[:-1]
values = np.concatenate(df.col_name.values)
f, u = pd.factorize(values)
b = np.bincount(f)
r = np.arange(len(df)).repeat(lens)
m = (b >= 3)[f]
new_splits = splits - np.bincount(r, ~m).astype(int).cumsum()[:-1]
new_values = np.split(values[m], new_splits)
df.assign(col_name=new_values)
col_name
0 [4]
1 [5, 1, 2]
2 [4, 5]
3 [1, 2, 4]
4 [5, 4, 1, 2]
【讨论】:
【参考方案2】:您可以通过value_counts
和boolean indexing
获得所有低于阈值的值:
from itertools import chain
a = pd.Series(list(chain.from_iterable(df['col_name']))).value_counts()
a = a.index[a >= 3]
print (a)
Int64Index([4, 5, 2, 1], dtype='int64')
df = df.applymap(lambda x: [v for v in x if v in a])
print (df)
col_name
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
【讨论】:
很好,我暂时想到了预过滤,整洁的先生。 @JohnGalt - 谢谢。np.concatenate(df.col_name.tolist())
应该比 itertools 快得多。
也许在小数据中,在大数据中不是。检查this
是的,我第一次也很惊讶。而sum
是最糟糕的解决方案 - 非常慢。【参考方案3】:
首先获取counts
,然后获取apply
或applymap
您对元素的条件检查。
In [2707]: counts = pd.Series([v for x in df.col_name for v in x]).value_counts()
In [2708]: df.col_name.apply(lambda x: [v for v in x if counts[v] >= 3])
Out[2708]:
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
Name: col_name, dtype: object
In [2709]: df.applymap(lambda x: [v for v in x if counts[v] >= 3])
Out[2709]:
col_name
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
详情
In [2710]: counts
Out[2710]:
4 4
5 3
2 3
1 3
30 1
888 1
52 1
16 1
75 1
99 1
dtype: int64
【讨论】:
【参考方案4】:您可以从collections
使用Counter()
:
import pandas as pd
from collections import Counter
limit = 3
df = pd.DataFrame('col_name': [[16, 4, 30], [5, 1, 2], [4, 5, 52, 888], [1, 2, 4], [5, 99, 4, 75, 1, 2]])
flat = Counter([y for x in df.col_name for y in x])
desired = [k for k, v in flat.items() if v >= limit]
df['col_name'] = df['col_name'].apply(lambda x: [i for i in x if i in desired])
【讨论】:
【参考方案5】:类似于this,使用collections.Counter
(但独立开发,有一些优化);
from collections import Counter
c = Counter(pd.Series(np.concatenate(df.col_name.tolist())))
def foo(array):
return [x for x in array if c[x] >= 3]
df.col_name = df.col_name.apply(foo)
df
col_name
1 [4]
2 [5, 1, 2]
3 [4, 5]
4 [1, 2, 4]
5 [5, 4, 1, 2]
【讨论】:
以上是关于按条件从列表的数据框列中计算和删除元素的主要内容,如果未能解决你的问题,请参考以下文章