我有df,
key amount code year_month
CHQ 100 1000 201701
EFT 200 1000 201701
CHQ 300 2000 201702
CHQ 400 2000 201702
EFT 500 3000 201703
EFT 600 4000 201703我喜欢groupby code和year_month,然后在字典中对key进行聚合计数,因此结果如下
key
code year_month
1000 201701 {'CHQ':1, 'EFT':1}
2000 201702 {'CHQ':2, 'EFT':0}
3000 201703 {'CHQ':0, 'EFT':2}然后将上述df转换为以code值作为行标签、year_month值作为列标签的矩阵,并在第0行和第0列上进行聚合,例如,对于所有code和year_month,(1,1)和count,(1,2)对201701进行count之和。
0 1 2 3 4
0 -1 0 201701 201702 201703
1 0 {'CHQ':3,'EFT':3} {'CHQ':1,'EFT':1} {'CHQ':2,'EFT':0} {'CHQ':0,'EFT':2}
2 1000 {'CHQ':1,'EFT':1} {'CHQ':1,'EFT':1} {'CHQ':0,'EFT':0} {'CHQ':0,'EFT':0}
3 2000 {'CHQ':2,'EFT':0} {'CHQ':0,'EFT':0} {'CHQ':2,'EFT':0} {'CHQ':0,'EFT':0}
4 3000 {'CHQ':0,'EFT':2} {'CHQ':0,'EFT':0} {'CHQ':0,'EFT':0} {'CHQ':0,'EFT':2} 发布于 2018-05-16 16:52:14
defaultdict
from collections import defaultdict
a = dict.fromkeys(df.key.unique(), 0)
b = defaultdict(lambda:defaultdict(lambda:a))
for t in df.itertuples():
b[t.year_month][t.code][t.key] += 1
b['All'][t.code][t.key] += 1
b[t.year_month]['All'][t.key] += 1
idx = df.code.unique().tolist() + ['All']
idx = pd.CategoricalIndex(idx, idx, ordered=True)
col = df.year_month.unique().tolist() + ['All']
col = pd.CategoricalIndex(col, col, ordered=True)
m = pd.DataFrame(b, idx, col)
m = m.mask(m.isna(), a)
m
201701 201702 201703 All
1000 {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9}
2000 {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9}
3000 {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9}
4000 {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9}
All {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9} {'CHQ': 9, 'EFT': 9}用append和concat求和
d = df.groupby(['code', 'year_month', 'key']).size().unstack([1, 2], fill_value=0)
d = d.reindex(columns=pd.MultiIndex.from_product(d.columns.levels), fill_value=0)
def add_level(d, k):
return pd.concat([d], keys=[k], axis=1)
m = d.join(d.sum(1, level=1).pipe(add_level, k='All')).pipe(
lambda d: d.append(d.sum().rename('All'))
)
m.groupby(axis=1, level=0).apply(lambda x: pd.Series(x.xs(x.name, 1).to_dict('i')))
201701 201702 201703 All
1000 {'CHQ': 1, 'EFT': 1} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 1, 'EFT': 1}
2000 {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0}
3000 {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 1}
4000 {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 1}
All {'CHQ': 1, 'EFT': 1} {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 2} {'CHQ': 3, 'EFT': 3}交替to_dict
pd.DataFrame({
j: {
i: m.loc[i, j].to_dict()
for i in m.index
} for j in m.columns.levels[0]
})旧答案
import pandas as pd
from collections import Counter
d = df.groupby(['code', 'year_month', 'key']).size().unstack(fill_value=0).assign(
key=lambda d: d.to_dict('r')
)[['key']].reset_index().rename_axis(None, 1).assign(key=lambda d: list(map(Counter, d.key)))
d
code year_month key
0 1000 201701 {'CHQ': 1, 'EFT': 1}
1 2000 201702 {'CHQ': 2, 'EFT': 0}
2 3000 201703 {'CHQ': 0, 'EFT': 1}
3 4000 201703 {'CHQ': 0, 'EFT': 1}然后
fv = Counter(dict.fromkeys(df.key.unique(), 0))
d2 = d.pivot_table(
'key', 'code', 'year_month', 'sum',
margins=True
).pipe(lambda d: d.mask(d.isna(), fv))
d2
year_month 201701 201702 201703 All
code
1000 {'CHQ': 1, 'EFT': 1} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 1, 'EFT': 1}
2000 {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0}
3000 {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 1}
4000 {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 1}
All {'CHQ': 1, 'EFT': 1} {'CHQ': 2, 'EFT': 0} {'EFT': 2} {'CHQ': 3, 'EFT': 3}我们可以破译一些东西让事情开始运作。我对此感觉不太好,但也许这足以满足你的需要。
使用Counter.update在位置上更改对象
d2.applymap(lambda x: x.update(fv));
d2
year_month 201701 201702 201703 All
code
1000 {'CHQ': 1, 'EFT': 1} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 1, 'EFT': 1}
2000 {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0}
3000 {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 1}
4000 {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 1}
All {'CHQ': 1, 'EFT': 1} {'CHQ': 2, 'EFT': 0} {'EFT': 2, 'CHQ': 0} {'CHQ': 3, 'EFT': 3}发布于 2018-05-17 18:00:46
如果我说对了的话,这就是我的想法:
df_tmp = (df.groupby(['code', 'year_month'])['key']
.agg(lambda x: (Counter(x),))
.to_frame()
.reset_index()
)
# Pivot
df_pivot = pd.pivot_table(df_tmp, index='code', columns='year_month', values='key', aggfunc=np.sum)
# Filling the gaps
df_final = df_pivot.apply(lambda x: [v[0] if v is not None else Counter(dict.fromkeys(df.key, 0)) for v in x ])请注意,(Counter(x),)函数中的这个tupeling“agg”是一个小的变通方法,因为熊猫似乎不喜欢处理对象。但是,无论如何,这将在最后的apply调用中被打开。
由于您有Counter实例,所以可以对列/行进行求和,并获得正确的结果:
df_final.sum(1)复制粘贴示例
from io import StringIO
import pandas as pd
import numpy as np
from collections import Counter
data = """
key amount code year_month
CHQ 100 1000 201701
EFT 200 1000 201701
EFT 200 1000 201702
EFT 200 1000 201702
EFT 200 1000 201702
EFT 200 1000 201702
EFT 200 1000 201702
CHQ 300 2000 201702
CHQ 400 2000 201702
EFT 500 3000 201703
EFT 600 4000 201703
"""
df = pd.read_csv(StringIO(data), sep='\s+')
df_tmp = (df.groupby(['code', 'year_month'])['key']
.agg(lambda x: (Counter(x),))
.to_frame()
.reset_index()
)
# Pivot
df_pivot = pd.pivot_table(df_tmp, index='code', columns='year_month', values='key', aggfunc=np.sum)
# Filling in the gaps
df_final = df_pivot.apply(lambda x: [v[0] if v is not None else Counter(dict.fromkeys(df.key, 0)) for v in x ])https://stackoverflow.com/questions/50375813
复制相似问题