使用场景和功能
对数据分类,然后再进行处理是常用的功能。在pandas中可以通过数据的分箱和聚合来实现。本文通过pandas实现相关功能,并封装为函数。整个过程分为四步:
1、形成分箱数据。
bins :整数,标量序列或者间隔索引,是进行分组的依据,
如果填入整数n,则表示将x中的数值分成等宽的n份(即每一组内的最大值与最小值之差约相等);
如果是标量序列,序列中的数值表示用来分档的分界值
如果是间隔索引,“ bins”的间隔索引必须不重叠
right :布尔值,默认为True表示包含最右侧的数值
当“ right = True”(默认值)时,则“ bins”=[1、2、3、4]表示(1,2],(2,3],(3,4]
当bins是一个间隔索引时,该参数被忽略。
bin 必须是递增的,否则会报错
pd.cutbins must increase monotonically
通过
if bin_max>stop:
bin.append(bin_max)
为了减少手动输入的工作量,通过range函数使用起始和步长生成等分的分箱数据。其中最后一个分箱使用range的stop和最大值。对于非等分的分箱,可以手工指定分箱数据。
np.arange(),对于步长为小数的情况,可以采用np.arange()代替pandas的range()函数。
bin = [i for i in range(start, stop, step)]
bin_max = data.max() # pf.max()是一个Series
2、 制作分箱标签。通过fstring将各分箱数值转化为字符串。并对最后一个字符串有分箱值到最大值改为更易于阅读的大于分箱值
n = len(bin)
labels = [f"{bin[i]}-{bin[i + 1]}" for i in range(n - 1)]
#将最后一个标签从分箱值到最大值改为大于分箱值,例如:最后一个标签为80(分箱值)-198.231(最大值),改为 >80
labels[-1]=f">{bin[-2]}"
3、使用pandas中cut函数,添加标签,增加分箱列,形成新数据
关于标签部分可以修改格式
默认格式为(30,50],直接使用df[‘bin’] = pd.cut(data, bins=bin)即可,但是数据的分箱标签需要和分箱一致。往往最后一个值不满足要求。
可以通过labels参数来控制标签格式。
格式1、采用—来连接,如30-50
labels = [f"{bin[i]}-{bin[i + 1]}" for i in range(n - 1)]
格式2、采用(,]来连接,如(30,50]
labels=[f"({bin[i]},{bin[i + 1]}]"for i in range(n - 1)]
将最后一个标签从 分箱值到最大值改为大于分箱值,例如:最后一个标签为80(分箱值)-198.231(最大值),改为 >80
labels[-1]=f">{bin[-2]}"
df['bin'] = pd.cut(data, bins=bin, labels=labels)
左边第一个区间是闭区间,其他区间为左开右闭区间
df.loc[df[col_name] ==bin[0], 'bin'] = labels[0]
对于前四步,已经封装为函数,方便调用。
#bin_cut将数据分箱。其中col_name要分箱的列,start,stop,step分别是始、终和步长,均为整数,其中stop无需和data的最大值对齐,已自适应。
def bin_cut(df,col_name,start,stop,step):
# 选择数据
data = df[col_name]
# 形成分段
bin = [i for i in range(start, stop, step)]
bin_max = data.max() # pf.max()是一个Series
if bin_max>stop:
bin.append(bin_max)
# 形成标签
n = len(bin)
labels = [f"{bin[i]}-{bin[i + 1]}" for i in range(n - 1)]
#将最后一个标签从 分箱值到最大值改为大于分箱值,例如:最后一个标签为80(分箱值)-198.231(最大值),改为 >80
labels[-1]=f">{bin[-2]}"
#添加标签,增加分箱列,形成新数据
df['bin'] = pd.cut(data, bins=bin, labels=labels)
#左边第一个区间是闭区间,其他区间为左开右闭区间
df.loc[df[col_name] ==bin[0], 'bin'] = labels[0]
return df
4、对分箱数据进行聚合。 按照分箱值进行分组,以累积分布为例,求出每个分箱”11月值“的累计值
通过求和项 聚合
def sum_cumsum(df):
#计算累积流量,注意不能用apply(sum)使用apply无法自动忽略空值
df=df.groupby('bin')[col].sum().reset_index()
#计算流量占比
s=df[col].sum()
df["占比"]=df[col].apply(lambda x : x/s)
df["累积占比"] = df["占比"].cumsum()
#重命名
df=df.rename(columns={col:"累积"+"-"+col,"占比":"占比"+"-"+col,"累积占比":"累积占比"+"-"+col})
return df
通过计数项 聚合
def count_cumsum(df):
dfs=df
#计算累积数量,注意不能用apply(sum)使用apply无法自动忽略空值
df=df.groupby('bin')[col].count().reset_index()
#计数非空行。.notnull()是布尔值,.sum()是True的数量,.count是总算
n=dfs[col].notnull().sum()
df["占比"]=df[col].apply(lambda x : x/n)
df["累积占比"] = df["占比"].cumsum()
#重命名
df=df.rename(columns={col:"累积计数"+"-"+col,"占比":"计数占比"+"-"+col,"累积占比":"累积计数占比"+"-"+col})
return df
整体代码
已支持小数步长
import os
import pandas as pd
from pandasrw import load,dump
#bin_cut将数据分箱。其中col_name要分箱的列,start,stop,step分别是始、终和步长,均为整数,其中stop无需和data的最大值对齐,已自适应。
def bin_cut(df,col_name,start,stop,step):
# 选择数据
data = df[col_name]
# 形成分箱数据
bin = [i for i in np.arange(start, stop, step)]
bin_max = data.max() # pf.max()是一个Series
if bin_max>stop:
bin.append(bin_max)
# 形成标签
n = len(bin)
#labels = [f"{bin[i]}-{bin[i + 1]}" for i in range(n - 1)]
labels=[f"({bin[i]},{bin[i + 1]}]"for i in range(n - 1)]
#将最后一个标签从 分箱值到最大值改为大于分箱值,例如:最后一个标签为80(分箱值)-198.231(最大值),改为 >80
labels[-1]=f">{bin[-2]}"
#添加标签,增加分箱列,形成新数据
df['bin'] = pd.cut(data, bins=bin, labels=labels)
#左边第一个区间是闭区间,其他区间为左开右闭区间
df.loc[df[col_name] ==bin[0], 'bin'] = labels[0]
return df
def sum_cumsum(df):
#计算累积流量,注意不能用apply(sum)使用apply无法自动忽略空值
df=df.groupby('bin')[col].sum().reset_index()
#计算流量占比
s=df[col].sum()
df["占比"]=df[col].apply(lambda x : x/s)
df["累积占比"] = df["占比"].cumsum()
#重命名
df=df.rename(columns={col:"累积"+"-"+col,"占比":"占比"+"-"+col,"累积占比":"累积占比"+"-"+col})
return df
def count_cumsum(df):
dfs=df
#计算累积数量,注意不能用apply(sum)使用apply无法自动忽略空值
df=df.groupby('bin')[col].count().reset_index()
#计数非空行。.notnull()是布尔值,.sum()是True的数量,.count是总算
n=dfs[col].notnull().sum()
df["占比"]=df[col].apply(lambda x : x/n)
df["累积占比"] = df["占比"].cumsum()
#重命名
df=df.rename(columns={col:"累积计数"+"-"+col,"占比":"计数占比"+"-"+col,"累积占比":"累积计数占比"+"-"+col})
return df
if __name__ == '__main__':
path=r"xx"
path1=r"xx"
col="xx"
#计算
df=load(path, col_name=[col],sheetname='Sheet1')
pf=bin_cut(df,col, 0, 100, 10)
#pf["bin_cumsum"] = pf.groupby('bin')[col].cumsum()
df_t_sum = sum_cumsum(pf)
df_t_count = count_cumsum(pf)
df_t=pd.concat([df_t_sum ,df_t_count],axis=1)
print(df_t)
df_t.to_excel(path1)
分不同的组,间隔10行写入excel
import xlwings as xw
import pandas as pd
from pandasrw import load,dump
#bin_cut将数据分箱。其中col_name要分箱的列,start,stop,step分别是始、终和步长,均为整数,其中stop无需和data的最大值对齐,已自适应。
def bin_cut(df,col_name,start,stop,step):
# 选择数据
data = df[col_name]
# 形成分箱数据
bin = [i for i in np.arange(start, stop, step)]
bin_max = data.max() # pf.max()是一个Series
if bin_max>stop:
bin.append(bin_max)
# 形成标签
n = len(bin)
#labels = [f"{bin[i]}-{bin[i + 1]}" for i in range(n - 1)]
labels=[f"({bin[i]},{bin[i + 1]}]"for i in range(n - 1)]
#将最后一个标签从 分箱值到最大值改为大于分箱值,例如:最后一个标签为80(分箱值)-198.231(最大值),改为 >80
labels[-1]=f">{bin[-2]}"
#添加标签,增加分箱列,形成新数据
df['bin'] = pd.cut(data, bins=bin, labels=labels)
# 左边第一个区间是闭区间,其他区间为左开右闭区间
df.loc[df[col_name] == bin[0], 'bin'] = labels[0]
return df
def sum_cumsum(df):
#计算累积流量,注意不能用apply(sum)使用apply无法自动忽略空值
df=df.groupby('bin')[col].sum().reset_index()
#计算流量占比
s=df[col].sum()
df["占比"]=df[col].apply(lambda x : x/s)
df["累积占比"] = df["占比"].cumsum()
#重命名
df=df.rename(columns={col:"累积"+"-"+col,"占比":"占比"+"-"+col,"累积占比":"累积占比"+"-"+col})
return df
def count_cumsum(df):
dfs=df
#计算累积数量,注意不能用apply(sum)使用apply无法自动忽略空值
df=df.groupby('bin')[col].count().reset_index()
#计数非空行。.notnull()是布尔值,.sum()是True的数量,.count是总算
n=dfs[col].notnull().sum()
df["占比"]=df[col].apply(lambda x : x/n)
df["累积占比"] = df["占比"].cumsum()
#重命名
df=df.rename(columns={col:"累积计数"+"-"+col,"占比":"计数占比"+"-"+col,"累积占比":"累积计数占比"+"-"+col})
return df
def run(df,sht):
i = 1
for name, pf in df.groupby("组"):
pf = pf[[col]]
pf = bin_cut(pf, col, 0, 100, 10)
print(pf)
# pf["bin_cumsum"] = pf.groupby('bin')[col].cumsum()
df_t_sum = sum_cumsum(pf)
df_t_count = count_cumsum(pf)
df_t = pd.concat([df_t_sum, df_t_count], axis=1)
cell1 = "A" + str(i)
sht.range(cell1).value = name
cell = "B" + str(i)
sht.range(cell).value = df_t
i = i + 10
return df_t
if __name__ == '__main__':
path=r"xx"
path1=r"xx"
col="xx"
# 启动Excel程序,不新建工作表薄(否则在创建工作薄时会报错),这时会弹出一个excel
app = xw.App(visible=True, add_book=False)
# 新建一个工作簿,默认sheet为Sheet1
wb = app.books.add()
# 将工作表赋值给sht变量
sht = wb.sheets('Sheet1')
#加载
df=load(path,sheetname='Sheet1')
#计算
run(df, sht)
wb.save(path1)