使用 pandas 和集合计算每列中某些值的数量

Counting number of certain values in each column using pandas and collections

我有一个包含 9 列和 6 行的 txt 文件。前 8 列是以下值之一:“1”、“2”和“3”。我将这些列命名为“A”到“H”。我将最后一列命名为:“class”。 最后一列是名称:“HIGH”。这是 txt 文件 (data.txt):

1,1,1,1,2,1,1,3,HIGH 
1,1,1,2,2,1,1,3,HIGH 
1,1,1,1,1,1,1,3,HIGH 
1,1,1,2,1,1,1,3,HIGH 
1,1,1,3,2,1,1,3,HIGH 
1,1,1,2,1,2,1,3,HIGH

我正在尝试计算每列中每个值的数量并打印一个列表,该列表应包含 3 个组件,包括该列中“1”、“2”和“3”值的数量 分别。例如,在第一列(例如 A)中,所有值都是“1”。我希望得到:A:[6,0,0]。对于所有值为“3”的第 8 列(例如 H),我希望得到:H : [0,0,6] 或对于第四列(例如 D)我有两个“1”,三个“2”和一个“3”。所以我期望:D:[2,3,1]。我尝试使用 pandascollection 来完成它。这是我所做的:

import pandas as pd
from collections import Counter

df = pd.read_csv('data.txt')
df.columns = ['A','B','C','D','E','F','G','H','class']

X = df.ix[:, 0:8].values
y = df.ix[:, 8].values

deg = ['HIGH']

names = ['A','B','C','D','E','F','G','H']

for j in range(0, 8):

    freqs = Counter(X[y == deg[0], j])
    print(names[j],':',list(freqs.values()))

以上代码的输出是空列表。这是它 returns:

A : []
B : []
C : []
D : []
E : []
F : []
G : []
H : []

如何修改上面的代码来得到我想要的? 谢谢!

使用pandas.Series.value_counts

df.loc[:, :"H"].apply(pd.Series.value_counts).fillna(0).to_dict("l")

输出:

{'A': [6.0, 0.0, 0.0],
 'B': [6.0, 0.0, 0.0],
 'C': [6.0, 0.0, 0.0],
 'D': [2, 3, 1],
 'E': [3.0, 3.0, 0.0],
 'F': [5.0, 1.0, 0.0],
 'G': [6.0, 0.0, 0.0],
 'H': [0.0, 0.0, 6.0]}

集合的解决方案是 select 所有没有最后的列,将 Counter 转换为 Series,因此输出为 DataFrame,用 DataFrame.fillna, convert values to integers and last to dictionary by DataFrame.to_dict 替换缺失值:

from collections import Counter

d = (df.iloc[:, :-1].apply(lambda x: pd.Series(Counter(x)))
       .fillna(0)
       .astype(int)
       .to_dict("list"))
print (d)
{'A': [6, 0, 0], 'B': [6, 0, 0], 
 'C': [6, 0, 0], 'D': [1, 4, 1], 
 'E': [3, 3, 0], 'F': [5, 1, 0], 
 'G': [6, 0, 0], 'H': [0, 0, 6]}

只有 pandas 解决方案 pandas.value_counts:

d = (df.iloc[:, :-1].apply(pd.value_counts)
       .fillna(0)
       .astype(int)
       .to_dict("list"))
print (d)
{'A': [6, 0, 0], 'B': [6, 0, 0],
 'C': [6, 0, 0], 'D': [2, 3, 1], 
 'E': [3, 3, 0], 'F': [5, 1, 0], 
 'G': [6, 0, 0], 'H': [0, 0, 6]}

在 python 内工作,因为您的最终结果是字典:

from string import ascii_uppercase
from collections import Counter, defaultdict
from itertools import chain, product
import csv    

d = defaultdict(list)
fieldnames = ascii_uppercase[:9]
# test.csv is your file above
with open('test.csv') as csvfile:    
    reader = csv.DictReader(csvfile, fieldnames = list(fieldnames))
    reader = Counter(chain.from_iterable(row.items() for row in reader))
    for col, value in product(fieldnames, ("1","2","3")):
        if col != fieldnames[-1]:
            d[col].append(reader.get((col,value), 0))      

打印(d)

defaultdict(list,
            {'A': [6, 0, 0],
             'B': [6, 0, 0],
             'C': [6, 0, 0],
             'D': [2, 3, 1],
             'E': [3, 3, 0],
             'F': [5, 1, 0],
             'G': [6, 0, 0],
             'H': [0, 0, 6]})

定义以下函数:

def cntInts(col):
    vc = col.value_counts()
    return [ vc.get(i, 0) for i in range(1,4) ]

然后应用它并打印结果:

for k, v in df.loc[:, 'A':'H'].apply(cntInts).iteritems():
    print(f'{k}: {v}')

对于你的数据样本,我得到了:

A: [6, 0, 0]
B: [6, 0, 0]
C: [6, 0, 0]
D: [2, 3, 1]
E: [3, 3, 0]
F: [5, 1, 0]
G: [6, 0, 0]
H: [0, 0, 6]

或者调用就足够了:

df.loc[:, 'A':'H'].apply(cntInts)

这次的结果是 系列 ,打印结果为:

A    [6, 0, 0]
B    [6, 0, 0]
C    [6, 0, 0]
D    [2, 3, 1]
E    [3, 3, 0]
F    [5, 1, 0]
G    [6, 0, 0]
H    [0, 0, 6]
dtype: object

编辑

根据您的评论,我认为您的数据有问题。

追根溯源:

  1. 定义一个字符串变量:

     txt = '''1,1,1,1,2,1,1,3,HIGH 
     1,1,1,2,2,1,1,3,HIGH 
     1,1,1,1,1,1,1,3,HIGH 
     1,1,1,2,1,1,1,3,HIGH 
     1,1,1,3,2,1,1,3,HIGH 
     1,1,1,2,1,2,1,3,HIGH'''
    
  2. 运行:

     import io
     df = pd.read_csv(io.StringIO(txt), names=['A','B','C','D','E','F','G','H','class'])
    
  3. 运行 我的代码在 我的 数据上。结果应该和预期的一样。

  4. 然后再次阅读你的输入文件(也进入df)和运行我的代码。 您的数据和我的数据之间可能存在一些差异。 特别是在你的输入文件中寻找任何额外的空间, 还要检查列类型(在 read_csv 之后)。