字典转计数表

3
我现在已经生成了一个类似于字典的东西,它看起来像这样:
{'G1':['PF101','PF201','PF204','PF101'],'G2':['PF101','PF202'],'G3':
['PF202','PF204','PF305'],'G4':['PF101','PF305','PF305','PF201']}

我希望使用它来创建一个制表符分隔的计数表,看起来像这样:
      PF101  PF201  PF204  PF202  PF305
G1      2      1       1      0     0
G2      1      0       0      1     0
G3      0      0       0      1     1
G4      1      1       0      0     2

我似乎找不到一种有效的方法来做这件事,所以任何建议都将非常有帮助。(参考实际字典有约2,000个键)

3个回答

7

Well, I had no idea this was a general python question (and not pandas)... well, what do you know, pandas fits in perfectly here!

If you don't have it, please install it. It's meant for stuff like this.

pip install pandas

选项1a
您可以逐列构建数据框,并在结果上调用melt。最后,使用pd.crosstab计算计数。

import pandas as pd

v = pd.concat([pd.Series(v, name=k) for k, v in d.items()], 1).melt()
pd.crosstab(v.variable, v.value)


value     PF101  PF201  PF202  PF204  PF305
variable                                   
G1            2      1      0      1      0
G2            1      0      1      0      0
G3            0      0      1      1      1
G4            1      1      0      0      2

这里,d 是你的输入字典。 选项1b
或者,使用pd.DataFrame.from_dict来加载你的数据;其余代码相同,只需在指定列名的点上稍微更改meltcrosstab的语法。
v = pd.DataFrame.from_dict(d, orient='index').reset_index().melt('index')
pd.crosstab(v['index'], v.value)

value  PF101  PF201  PF202  PF204  PF305
index                                   
G1         2      1      0      1      0
G2         1      0      1      0      0
G3         0      0      1      1      1
G4         1      1      0      0      2

选项2 使用stack + str.get_dummies的另一种选项:
pd.DataFrame.from_dict(d, orient='index')\
  .stack()\
  .str.get_dummies()\
  .sum(level=0)\
  .sort_index()

    PF101  PF201  PF202  PF204  PF305
G1      2      1      0      1      0
G2      1      0      1      0      0
G3      0      0      1      1      1
G4      1      1      0      0      2

使用pd.get_dummies也可以得到类似的解决方案,尽管我敢打赌str.get_dummies会稍微快一些。

v = pd.DataFrame.from_dict(d, orient='index')\
      .stack()\
      .reset_index(level=1, drop=True)

pd.get_dummies(v).sum(level=0)

    PF101  PF201  PF202  PF204  PF305
G2      1      0      1      0      0
G3      0      0      1      1      1
G1      2      1      0      1      0
G4      1      1      0      0      2

选项三:使用 get_dummies + dot。这是我从 piRSquared 学到的技巧。
v = pd.DataFrame.from_dict(d, orient='index').stack()
pd.get_dummies(v.index.get_level_values(0)).T.dot(pd.get_dummies(v.values))

    PF101  PF201  PF202  PF204  PF305
G1      2      1      0      1      0
G2      1      0      1      0      0
G3      0      0      1      1      1
G4      1      1      0      0      2

详情
它的作用是从索引和值中创建 OHEs。

pd.get_dummies(v.index.get_level_values(0))

    G1  G2  G3  G4
0    0   1   0   0
1    0   1   0   0
2    0   0   1   0
3    0   0   1   0
4    0   0   1   0
5    1   0   0   0
6    1   0   0   0
7    1   0   0   0
8    1   0   0   0
9    0   0   0   1
10   0   0   0   1
11   0   0   0   1
12   0   0   0   1

并且,
pd.get_dummies(v.values)

    PF101  PF201  PF202  PF204  PF305
0       1      0      0      0      0
1       0      0      1      0      0
2       0      0      1      0      0
3       0      0      0      1      0
4       0      0      0      0      1
5       1      0      0      0      0
6       0      1      0      0      0
7       0      0      0      1      0
8       1      0      0      0      0
9       1      0      0      0      0
10      0      0      0      0      1
11      0      0      0      0      1
12      0      1      0      0      0

最后,找到这两个张量的点积,结果是交叉制表。

2
谢谢!这个完美地运作了,我从来不知道pandas可以做到这一切。 - eric
@eric 等一下... 这不是个关于Pandas的问题吗?xD 好吧,现在你知道可以在这里使用Pandas了,所以你很欢迎 :) - cs95
@eric 嗯,不止这三个选项。例如,您还可以使用 pivot 或 pivot_table。但是对于您的目的来说,这三个选项已经足够了。 - cs95

2

虽然 pandas 是解决这个问题的最佳方案,但您也可以创建一个类来表示您的数据:

class Table:
   def __init__(self, table):
      self.table = table
      self.headers = sorted(set([i for b in self.table.values() for i in b]), key=lambda x:int(x[2:]))
      self.full_table = {a:[(i, b.count(i)) for i in self.headers] for a, b in self.table.items()}
   @property
   def structure(self):
      return self.full_table
   def __repr__(self):
       return '\t'+'\t'.join(self.headers)+'\n'+'\n'.join("{}\t{}".format(a, '\t'.join(map(lambda x:str(x[-1]), b))) for a, b in sorted(self.full_table.items(), key=lambda x:x[0]))

>>>Table(d)

        PF101   PF201   PF202   PF204   PF305
  G1    2       1       0       1       0
  G2    1       0       1       0       0
  G3    0       0       1       1       1
  G4    1       1       0       0       2

1
这是使用纯Python的另一种方法:
from collections import Counter

d = {'G1':['PF101','PF201','PF204','PF101'],'G2':['PF101','PF202'],
     'G3': ['PF202','PF204','PF305'],'G4':['PF101','PF305','PF305','PF201']}

columns = ['PF101','PF201','PF202', 'PF204', 'PF305']

table = ['\t' + '\t'.join(columns) + '\n']

for key, value in sorted(d.items()):
    temp = [key]
    counts = Counter(value)

    for col in columns:
        if col not in counts:
            counts[col] = 0

    temp.extend([str(v) for _, v in sorted(counts.items())])

    table.append('\t\t'.join(temp) + '\n')

print(''.join(table))

输出什么:

    PF101   PF201   PF202   PF204   PF305
G1      2       1       0       1       0
G2      1       0       1       0       0
G3      0       0       1       1       1
G4      1       1       0       0       2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接