按条件从列表的数据框列中计算和删除元素

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次。

我尝试使用来自collectionsCounter,但没有成功。

如果你能给我任何提示,我真的很感激。提前致谢。

【问题讨论】:

【参考方案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_countsboolean 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,然后获取applyapplymap 您对元素的条件检查。

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]

【讨论】:

以上是关于按条件从列表的数据框列中计算和删除元素的主要内容,如果未能解决你的问题,请参考以下文章

从列表列表中提取元素并将其分配为熊猫数据框列中的值

不同数据框列中的 zip 列表元素

循环遍历 pandas 数据框列中的列表元素以在新列中返回列表

计算数据框列中列表中单词的出现次数

如何从 pyspark 数据框列中的列表中删除特定字符串

从 Spark 数据框列中 ArrayType 类型的行中获取不同的元素