如何解决pd.groupby在另一个groupby上,转置pd.cut
关于Pandas及其groupby和cut函数,我仍然遇到另一个相当复杂的问题。情况如下,假设我有一个看起来像这样的DataFrame:
import Pandas as pd
pd.DataFrame(data)
A B C ipv4
0 1 3 3 0.0.0.0
1 2 2 1 140.0.0.0
2 3 1 3 230.0.0.0
3 1 1 2 140.0.0.0
4 3 1 2 NaN
在这一点上,我必须补充一点,我在这里使用的实际DataFrame可以包含数百万(!)行,因此性能是我在这里要牢记的一点。
我已经制作了一个函数,该函数赋予了我A,B和C的幂集,因此pset = [(A),(B),(C),(A,B),... ]
如果没有空白,您就会明白。我现在将这些组合中的每一个组合成一个循环,并为每个组合创建一个count_df
,如下所示:
for combination in pset:
df.groupby(list(combination))
count_df = df.size().reset_index().rename(columns={0: 'count'})
print(count_df)
A count
0 1 2
1 2 1
2 3 2
...
A B count
0 1 1 1
1 1 3 1
2 2 2 1
3 3 1 2
...
我们的问题越来越近:我需要将IP classes的一些非常基本的信息及其各自的ABC组合添加到count_df
的每一行中(您可以在提供的内容中向下滚动链接到高阶位(HOB)并查看表格,以快速了解我要在此处执行的操作)。我为此在df
中添加了另一行,其中包含每行ipv4的第一个八位位组,并使用了Pandas的cut来非常快地获取每个间隔的计数:
# I use 256 as value for any row that has "NaN" instead of a real address
df["ipv4"].replace(to_replace="NaN",value="256.0.0.0",inplace=True)
df["first_octet"] = df["ipv4"].apply(lambda x: int(x.partition(".")[0]))
df["cut_group"] = pd.cut(data["first_octet"],[0,127,191,223,239,255,256])
print(df)
A B C ipv4 first_octet cut_group
0 1 3 3 0.0.0.0 0 (0,127.0]
1 2 2 1 140.0.0.0 140 (127.0,191.0]
2 3 1 3 230.0.0.0 230 (223.0,239.0]
3 1 1 2 140.0.0.0 140 (127.0,191.0]
4 3 1 2 256.0.0.0 256 (255.0,256.0]
for combination in pset:
df.groupby(list(combination) + ["cut_group"])
count_df = df.size().reset_index().rename(columns={0: 'count'})
print(count_df)
A cut_group count
0 1 (0,127] 1
1 1 (127,191] 1
2 1 (191,223] 0
3 1 (223,239] 0
4 1 (239,255] 0
5 1 (255,256] 0
6 2 (0,127] 0
7 2 (127,191] 1
8 2 (191,223] 0
9 2 (223,239] 0
10 2 (239,255] 0
11 2 (255,256] 0
12 3 (0,127] 0
13 3 (127,191] 0
14 3 (191,223] 0
15 3 (223,239] 1
16 3 (239,255] 0
17 3 (255,256] 1
...
A B cut_group count
0 1 1 (0,127] 0
1 1 1 (127,191] 1
2 1 1 (191,223] 0
3 1 1 (223,239] 0
4 1 1 (239,255] 0
5 1 1 (255,256] 0
6 1 2 (0,127] 0
7 1 2 (127,191] 0
8 1 2 (191,223] 0
9 1 2 (223,239] 0
10 1 2 (239,255] 0
11 1 2 (255,256] 0
12 1 3 (0,127] 1
13 1 3 (127,191] 0
14 1 3 (191,223] 0
15 1 3 (223,239] 0
16 1 3 (239,255] 0
17 1 3 (255,256] 0
18 2 1 (0,127] 0
19 2 1 (127,191] 0
20 2 1 (191,223] 0
21 2 1 (223,239] 0
22 2 1 (239,255] 0
23 2 1 (255,256] 0
24 2 2 (0,127] 0
25 2 2 (127,191] 1
26 2 2 (191,223] 0
27 2 2 (223,239] 0
28 2 2 (239,255] 0
29 2 2 (255,256] 0
30 2 3 (0,127] 0
31 2 3 (127,191] 0
32 2 3 (191,223] 0
33 2 3 (223,239] 0
34 2 3 (239,255] 0
35 2 3 (255,256] 0
36 3 1 (0,127] 0
37 3 1 (127,191] 0
38 3 1 (191,223] 0
39 3 1 (223,239] 1
40 3 1 (239,255] 0
41 3 1 (255,256] 1
42 3 2 (0,127] 0
43 3 2 (127,191] 0
44 3 2 (191,223] 0
45 3 2 (223,239] 0
46 3 2 (239,255] 0
47 3 2 (255,256] 0
48 3 3 (0,127] 0
49 3 3 (127,191] 0
50 3 3 (191,223] 0
51 3 3 (223,239] 0
52 3 3 (239,255] 0
53 3 3 (255,256] 0
...
好的,所以我现在缺少下一步。我需要的是对于pset的每种组合的输出,如下所示:
for combination in pset:
<???>
print(count_df)
A count (0,127] (127,191] (191,223] (223,239] (239,255] (255,256]
0 1 2 1 1 0 0 0 0
1 2 1 0 1 0 0 0 0
2 3 1 0 0 0 1 0 1
...
A B count (0,256]
0 1 1 1 0 1 0 0 0 0
1 1 2 0 0 0 0 0 0 0
2 1 3 1 1 0 0 0 0 0
3 2 1 0 0 0 0 0 0 0
4 2 2 1 0 1 0 0 0 0
5 2 3 0 0 0 0 0 0 0
6 3 1 2 0 0 0 1 0 1
7 3 2 0 0 0 0 0 0 0
8 3 3 0 0 0 0 0 0 0
...
我不确定该怎么做。为了澄清,count_df
的列也可以是A-B-C count classA classB classC classD classE classNaN
。 count
列需要指出有多少行具有ABC的单独组合的计数,就像我会打电话给df.groupby(list(combination)).size().reset_index().rename(columns={0: 'count'})
一样,interval列需要指出有多少行进行计数ABC个人组合的个人类别。您可以将问题概括为:与groupby1 = df.groupby(list(combination) + ["cut_group"])
一起使用groupby,然后在与之类似的另一个groupby2 = groupby1.groupby(list(combination))
上进行分组,然后将groupby1
中的类计数信息添加到转置的行中。这最后几行是无意义的代码,只是为了阐明我的意思。
对于在我的代码中填写提到的“空白”的任何建议,以及使用熊猫的其他我不知道的其他功能做一些建议的建议,我都愿意接受。与往常一样,我很高兴学习使用熊猫的不同方法。谢谢!
解决方法
您可以做的是join
的cut_group列中的pd.get_dummies
,然后在sum
中使用groupby
,例如:
# get dummies
dummies = pd.get_dummies(df["cut_group"])
df_ = df.join(dummies) #you can reassign to df if you want
for combination in pset:
gr = df_.groupby(list(combination)) #change to df if you reassign the join to df before
count_df = (gr.size().to_frame('count')
.join(gr[dummies.columns].sum())
)
print(count_df)
,
您的pd.cut
有一个小错误:它会忽略第一个八位位组为0的行,因为默认情况下它不包括下边缘。从-1开始启动垃圾桶。
df["first_octet"] = df["ipv4"].str.split('.',expand=True)[0].fillna(256).astype('int')
# Pivoting as CategoricalDType takes forever. I think this is a bug in pandas.
# Converting to string to make pivot_table faster
df["cut_group"] = pd.cut(df["first_octet"],[-1,127,191,223,239,255,256]).astype('str')
首先,对所有三列(A,B,C)运行数据透视,
tmp = pd.pivot_table(df,index=['A','B','C'],columns='cut_group',values='ipv4',aggfunc='count',fill_value=0).rename_axis(None,axis=1)
tmp['count'] = tmp.sum(axis=1)
然后,您可以对3的任何组合进行分组和求和:
from itertools import combinations
cols = list('ABC')
pset = []
for size in [1,2,3]:
pset += [list(c) for c in combinations(cols,size)]
# pset = [['A'],['B'],['C'],['A','B'],['B','C']]
result = [tmp.groupby(p).sum() for p in pset]
执行时间取决于A,B和C的可变性。当存在更多不同的值时,它花费的时间更长。对于我的随机数据集(A,B,C是随机的1-1000、500万行),大约需要40秒。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。