pandas数据的分箱和聚合

使用场景和功能

对数据分类,然后再进行处理是常用的功能。在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)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风暴之零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值