美文网首页
洞悉客户心声:Pandas标签帮你透视客户,标签化营销如虎添翼

洞悉客户心声:Pandas标签帮你透视客户,标签化营销如虎添翼

作者: 一只写程序的猿 | 来源:发表于2024-04-17 09:53 被阅读0次

    在构建高效的数据分析体系时,我们经常会遇到两个核心概念:指标(Metrics)标签(Tags)。指标是对业务性能的量化衡量,它们帮助我们追踪关键业务目标的达成情况。例如,我们可能会关注用户增长率、产品销售额或客户的增续投等指标。这些数字目标为我们提供了业务运行的直观快照,并允许我们对成功与否进行量化评估。

    而标签,则是附加在指标上的文字描述,用于对数据进行分类和上下文化。它们像是数据的属性,使我们能够从不同的角度切入,对指标进行深入分析。例如,对于销售额这一指标,我们可以使用“地区”、“产品线”、“客户群体”等标签来细分市场表现。通过这种方式,我们不仅能够了解整体的销售情况,还能够洞察到哪些产品在特定区域或客户群体中最受欢迎,从而做出更加精细化的业务决策。

    在实际应用中,指标和标签的关系可以类比于坐标系中的点和坐标轴。指标代表了我们要追踪的具体数值,而标签则定义了坐标系中的各个维度,帮助我们定位和解释这些数值。没有标签,我们只能得到一个孤立的数字;而有了标签,我们就能够将这个数字放置在一个多维度的空间中,理解其背后的含义和价值。

    客户标签不仅是数据价值体现的关键途径,更是我们今天讨论的核心所在。那在数据仓库中,通过分层、归类、建模会计算出一系列的指标,而标签则可以利用pandas将指标转化为对应的标签。

    上篇已经讲解了pandas的几个方法,很多朋友好奇学会了,适用的场景在哪呢,这就听我娓娓道来。

    先打个底:以理解为主,不够严谨,如果看完还是不会,那一定是我讲的不够好。我会尽可能对相对复杂的代码加入注释,欢迎大家评论区相互交流啊。

    一、对类别型指标进行值的替换

    该函数用于对类别型指标进行值的替换,举个例子,假设用户的星座对应的字典如下:
    "cnstll": {"白羊座":"11","金牛座":"12","双子座":"13","巨蟹座":"14","狮子座":"15","处女座":"16", "天枰座":"17","天蝎座":"18","射手座":"19","摩羯座":"20","水瓶座":"21","双鱼座":"22","其他":"99","NULL":"99"},

    import pandas as pd
    
    def cat_process(df, cat_dict):
        '''
        该函数用于对类别型指标进行值的替换,其中:
        df      : dataframe,传入待处理的dateframe,必须包括待替换的指标列
        cat_dict: dict类型,key代表待替换的指标名称,value代表用于替换的一一对应的值的关系词典
                关系词典中必须包含“其他”和"NULL"两个key。
                原数据中没有“其他”的在“其他”中填入和“NULL”key对应的相同的value
    
        return df:传入的df中在cat_dict中存在的指标的值已被替换成为对应标签的值
        '''
        for key,cat_val in cat_dict.items():
            # df[key] 取某一列的值   df[key].map(cat_val) 根据这个字典或函数对 Series(索引 + 值) 中的每个元素进行映射或转换
            df[key] = df[key].map(cat_val)
            # 将空值替换成其他,inplace = True: 表示对 DataFrame 进行原地修改,即不创建新的副本
            df[key].fillna(cat_val['其他'], inplace=True)
            # 将数据类型转换为int64
            df[key] = df[key].astype('int64')
        return df
    
    
    # 创建测试数据
    data = {
        '姓名': ['张三', '李四', '王五', '赵六', '钱七'],
        '星座': ['狮子座', '天蝎座', '双子座', '摩羯座', '水瓶座']
    }
    
    df = pd.DataFrame(data)
    
    # 星座的字典
    cat_dict = {
        "星座": {
            "白羊座": "11",
            "金牛座": "12",
            "双子座": "13",
            "巨蟹座": "14",
            "狮子座": "15",
            "处女座": "16",
            "天枰座": "17",
            "天蝎座": "18",
            "射手座": "19",
            "摩羯座": "20",
            "水瓶座": "21",
            "双鱼座": "22",
            "其他": "99",
            "NULL": "99"
        }
    }
    
    # 调用 cat_process 函数
    processed_df = cat_process(df, cat_dict)
    
    # 打印处理后的 DataFrame
    print(processed_df)
    

    运行结果

       姓名  星座
    0  张三  15
    1  李四  18
    2  王五  13
    3  赵六  20
    4  钱七  21
    

    可能会有人提出疑问:为什么要经历这样的转换过程?将中文值转换为数字岂非增加了复杂性?然而事实并非如此。采用数字存储具有以下几点好处:

    • 存储效率:数字通常比文本占用更少的存储空间。使用数字代码可以减少数据库的存储需求,提高存储效率。
    • 查询性能:数字类型的数据在数据库中进行查询和排序时比文本类型更快。这对于需要频繁进行数据分析和报告的大型数据集尤其重要。
    • 数据一致性:使用数字代码可以避免由于文本标签的不同写法(如大小写、空格、特殊字符等)引起的数据不一致问题。
    • 安全性:在某些情况下,将敏感信息(如客户信息)以数字代码的形式存储可以提高数据的安全性。
    • 数据处理:在进行数据分析和挖掘时,数字类型的数据更容易进行计算和统计,如使用聚合函数、执行数学运算等。
    • 扩展性:数字代码可以更容易地扩展以适应新的标签或分类,而不需要修改数据库结构。
    • 兼容性:数字代码可以与不同的数据库系统和数据分析工具兼容,便于数据的迁移和交换。
      总的来说,将客户标签存储为数字可以提高数据库的效率、节省存储空间,同时有助于保护客户隐私和简化数据处理过程。

    这一种方法适用于多种场景,比如客户来源、公司类型、居住省份、性别、生肖等等。

    二、对数值型指标进行缺失值的填充和分箱处理

    该函数用于对数值型指标进行缺失值的填充和分箱处理,举个例子,我们对客户的最后一笔交易距今时长做一个分箱处理:
    区间对应的字典值如下:

    标签编码 标签值
    11 1年以下
    12 [1年,3年)
    13 [3年以上)
    99 未投资

    那我们的执行代码如下:

    import pandas as pd
    import numpy as np
    
    def num_process(df, num_dict, num_null):
        '''
        该函数用于对数值型指标进行缺失值的填充和分箱处理,其中:
        df      : dataframe,传入待处理的dateframe,必须包括待分箱的指标列
        num_dict: dict类型,key代表待分箱的指标名称,value代表分箱的切分点
        tag_null:dict类型,key代表待分箱的指标名称,value代表该指标下需单独分组的缺失值
    
        return df:传入的df中在num_dict中存在的指标的值已被替换成为对应标签的值
        '''
        # num_dict:{'last_trans_mon_dur': [0, 12, 36, inf]}
    
        for key, cat_val in num_dict.items():
            null_ind = []
            if num_null.get(key) is not None:
                # 找到数据框中列为 key 且数值等于 num_null[key] 的行的索引,并转换为列表形式。 例如 [0, 1, 2] 此处代表找出和json文件中缺失值一样的数据对应的索引
                null_ind2 = list(df[df[key] == num_null[key]].index)  # 主要是找出 不正常的数据 脏数据, 如果数据质量不错,这里就不会执行
                # 将数据框中列为 key 且数值等于 num_null[key] 的值替换为 98。 其中 num_null里键key对应的值 inplace = True 代表在原数据上修改,默认为False
                df[key].replace(num_null[key], 98, inplace=True)
                null_ind.extend(null_ind2)  # 将列表往后扩展
                print("①.null_ind:%s" % null_ind)
            df[key] = df[key].astype('str')  # 将数据框类型转换为str
            df[key].replace('NULL', '99', inplace=True)  # 数据框中指标为 key 且数值等于 'NULL' 的值替换为 99。
            null_ind1 = list(
                df[df[key] == '99'].index)  # 找到数据框中列为 key 且数值等于 99 的行的索引,并转换为列表形式。 例如 [0, 1, 2] 目的也是为了找出null的数据
            null_ind.extend(null_ind1)
            this_col = df[key].astype('float')  # 将数据框类型转换为浮点型  例如 99 - 99.0
            print("②.this_col:%s" % this_col)
            print("☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆")
            
            unnull_ind = df.index.difference(null_ind)  # 查找两个索引的集合差异
            print("③.unnull_ind:%s" % unnull_ind)
            print("④.this_col[unnull_ind]:%s" % this_col[unnull_ind])
            print("⑤.cat_val,:%s" % cat_val)
            # 对非缺失值进行分箱处理,使用 pd.cut() 函数将数值分箱,参数 right=False 表示左闭右开区间,生成的标签从 11 开始递增。
            '''
            cat_val:[0, 12, 36, inf]
            range(11, 10+len(cat_val)):range 函数,生成一个从 11 开始、到 10+len(cat_val)-1 结束的整数序列。
            其中,len(cat_val) 表示分箱的数量,加上 10 是为了确保生成的标签与分箱的数量对应,并且从 11 开始编号。
            str(x) for 这部分是一个列表推导式的语法结构,表示对 range() 生成的每个元素 x 执行字符串化操作,并将结果组成一个新的列表。
    
            如果 cat_val 是 [0, 12, 36, float('inf')],那么生成的标签就是 ['11', '12', '13']。
    
            ['11', '12', '13'] 因为这一列为 last_trans_mon_dur 最后一笔交易距今时长 cat_val为[0, 12, 36, inf]
            所以pd.cut切割的区间为[0, 12), [12, 36), [36, float('inf')) 
                    对应的字典值为 1年以下 [1年,3年) [3年以上)
            '''
            this_col[unnull_ind] = pd.cut(this_col[unnull_ind], cat_val, right=False,
                                          labels=[str(x) for x in range(11, 10 + len(cat_val))])
            # this_col[unnull_ind] 此时为 last_trans_mon_dur 列转换后的数值 即从原来的月份小数转化为了 11 12 13
            df[key] = this_col.astype(
                'int64')  # 将last_trans_mon_dur的新值赋予df中last_trans_mon_dur这一列,同时转换为将数据类型转换为 int64
        return df
    
    # 创建测试数据
    data = {
        'name': ['张三', '李四', '王五', '赵六'],
        'last_trans_mon_dur': [2, 18, -2, 30]
    }
    df = pd.DataFrame(data)
    
    # 定义分箱字典和缺失值字典
    num_dict = {'last_trans_mon_dur': [0, 12, 36, np.inf]}
    num_null = {'last_trans_mon_dur': -2}
    
    # 处理数据
    processed_df = num_process(df, num_dict, num_null)
    
    # 打印结果
    print(processed_df)
    

    运行结果

    ①.null_ind:[2]
    ②.this_col:0     2.0
    1    18.0
    2    98.0
    3    30.0
    Name: last_trans_mon_dur, dtype: float64
    ☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆☆₊⁺🦴🐾₊⁺ ✩°。⋆✰⋆⁺₊⋆ †☾⋆
    ③.unnull_ind:Int64Index([0, 1, 3], dtype='int64')
    ④.this_col[unnull_ind]:0     2.0
    1    18.0
    3    30.0
    Name: last_trans_mon_dur, dtype: float64
    ⑤.cat_val,:[0, 12, 36, inf]
      name  last_trans_mon_dur
    0   张三                  11
    1   李四                  12
    2   王五                  98
    3   赵六                  12
    

    显而易见,结果已经发生了明显的变化:原本的具体数值已经转换为了标签编码。例如,原本数值为2个月的数据,现在对应的标签编码为11,代表着1年以下的时长。
    将指标转换为标签编码有几个好处:

    • 简化解释: 标签编码将原本复杂的数值转换为了易于理解的分类标签,使得数据解释更加直观和简单。
    • 降低误差: 通过将连续的数值转换为有限的分类,可以降低由于数据误差或测量不准确性而引起的影响。
    • 增强模型泛化能力: 在某些机器学习模型中,将指标转换为标签编码可以提高模型的泛化能力,使其更适应不同的数据分布和模式。
    • 方便数据分析: 标签编码使得数据更容易被聚合和比较,从而方便进行数据分析和可视化。
      这样表达更加清晰,也更容易理解。

    这一种方法适用于多种场景,比如用户年龄(少年、青年、中青年、中年、中老年、老年)、当前客户价值等级(优质活跃客户、优质潜力客户、优质维持客户、优质挽留客户、普通...、低价值...)、客户贡献度等级(高忠诚、一般忠诚、低忠诚)、当前客户贡献度等级环比趋势(上升、稳定、下降)等等。

    三、对数值型指标进行判断

    该函数用于对数值型指标进行判断,大于输入的词典中的阙值的判断为1,否则为0。
    举个例子,现在要判定客户是否为存续客户,如果存续金额>0则为1,否则为0。

    import pandas as pd
    
    def boo_process(df, boo_dict):
        '''
        该函数用于对数值型指标进行判断,大于输入的词典中的阙值的判断为1,否则为0,其中:
        df      : dataframe,传入待处理的dateframe,必须包括待处理的指标列
        boo_dict: dict类型,key代表待判断的指标名称,value代表该指标对应的阙值
    
        return df:传入的df中在num_dict中存在的指标的值已被替换成为对应标签的值
        '''
        for key, val in boo_dict.items():
            print("布尔指标:%s" % key)
            df[key].replace('NULL', 0, inplace=True)  # NULL值转换为0
            df[key] = (df[key] > val).astype('int64')  # 查找df[key]对应的值中 大于0的都标记为1
        return df
    
    
    # 生成测试数据
    data = {'姓名': ['张三', '李四', '王五'],
            '存续金额': [200000, 880000, 0]}
    df = pd.DataFrame(data)
    
    # 定义阈值字典 存续金额
    boo_dict = {'存续金额': 0}
    
    df_processed = boo_process(df, boo_dict)
    print(df_processed)
    

    运行结果

    布尔指标:存续金额
       姓名  存续金额
    0  张三     1
    1  李四     1
    2  王五     0
    

    显而易见,结果已经发生了明显的变化:原本的具体数值已经转换为了0和1。

    这一种方法适用于多种场景,比如历史最大交易金额(以100万为阈值,大于100万为1,反之为0)、是否购买过美妆(以美妆交易金额0为阈值,大于0为1,反之为0)等等。

    四、json文件配置及读取

    学习了上述三个方法后,您会发现其实传入的数据都是以 JSON 文件的形式提供的。为了降低后期的运维成本并提高代码的规范性,可以将需要处理的同类型指标统一放在 JSON 文件中。
    例如,可以创建几个 JSON 文件,文件名分别为:

    • 布尔型标签.json
     {
      "max_trans_amt": "最大交易金额",
      "max_trans_amt": 10000000
     }
    
    • 数值型标签.json
    {
         "his_max_aum_amt": "历史最大存续金额" 
        ,"his_max_aum_amt": [0,0.0001,1000000,3000000,6000000,10000000,30000000,60000000,100000000,Infinity]
        ,"year_old": "年龄"
        ,"year_old": [0, 18, 30, 45, 60, 75, Infinity]
    }
    
    • 特殊缺失值.json
    {
        "curr_hold_amt_mom":"当前存续资产金额环比"
        ,"curr_hold_amt_mom":-2
        ,"curr_hold_amt_yoy":"当前存续资产同比"
        ,"curr_hold_amt_yoy":-2
    }
    
    
    • 类别型标签.json
    {
        "gender":"性别",
        "gender" :{"男":"1","女":"0","其他":"9","NULL":"9"},
        "zodiac": "生肖",
        "zodiac": {"11":"11","12":"12","13":"13","14":"14","15":"15","16":"16",
            "17":"17","18":"18","19":"19","20":"20","21":"21","22":"22","其他":"99","NULL":"99"},
        "cnstll": "星座",
        "cnstll": {"白羊座":"11","金牛座":"12","双子座":"13","巨蟹座":"14","狮子座":"15","处女座":"16", 
            "天枰座":"17","天蝎座":"18","射手座":"19","摩羯座":"20","水瓶座":"21","双鱼座":"22","其他":"99","NULL":"99"},
        "ctry": "居民身份",
        "ctry": {"中国":"1","中国香港和中国澳门":"2","中国台湾":"3","其他":"4","NULL":"9"},
        "prov":  "居住省份",
        "prov":  {"北京" : "11","天津" : "12","河北" : "13","山西" : "14","内蒙古" : "15"
            ,"辽宁" : "21","吉林" : "22","黑龙江" : "23","上海" : "31","江苏" : "32","浙江" : "33"
            ,"安徽" : "34","福建" : "35","江西" : "36","山东" : "37","河南" : "41","湖北" : "42"
            ,"湖南" : "43","广东" : "44","广西" : "45","海南" : "46","重庆" : "50","四川" : "51"
            ,"贵州" : "52","云南" : "53","西藏" : "54","陕西" : "61","甘肃" : "62","青海" : "63"
            ,"宁夏" : "64","新疆" : "65","其他":"98","NULL":"99"},
        "city": "城市类别",
        "city": {"北京":"1","上海":"1","广州":"1","深圳":"1",
            "成都":"2","重庆":"2","杭州":"2","武汉":"2","西安":"2","天津":"2","苏州":"2","南京":"2","郑州":"2","长沙":"2","东莞":"2","沈阳":"2","青岛":"2","合肥":"2","佛山":"2",
            "宁波":"3","昆明":"3","福州":"3","无锡":"3","厦门":"3","济南":"3","大连":"3","哈尔滨":"3","温州":"3","石家庄":"3","泉州":"3","南宁":"3","长春":"3","南昌":"3","贵阳":"3","金华":"3",
            "常州":"3","嘉兴":"3","南通":"3","徐州":"3","太原":"3","珠海":"3","中山":"3","保定":"3","兰州":"3","台州":"3","绍兴":"3","烟台":"3","廊坊":"3","其他":"4","NULL":"9"},
        "mrtl_stat":"婚姻情况",
        "mrtl_stat":{ "02":"1","01":"2","其他":"9","NULL":"9"}
    }
    

    在任何情况下,都应该考虑下"Why",这样做的好处其实是多方面的:

    • 降低维护成本:将指标信息保存在外部 JSON 文件中,减少了直接修改代码的需求。如果需要新增或者修改指标,只需修改对应的 JSON 文件,不用改动代码逻辑,降低了维护成本。
    • 提高代码规范性:将指标信息和代码逻辑分离,使代码更加清晰易懂,符合良好的编程规范和设计原则,便于团队协作和后续维护。
    • 灵活性和可扩展性:通过 JSON 文件动态加载指标信息,使得系统更加灵活可配置,便于应对不同的需求和变化。新增指标或修改阈值只需更新 JSON 文件,无需修改代码,提高了系统的可扩展性和适应性。

    既然有了json文件,代码自然要增加一块读取的功能

    import os, json
    
    data_file_path_test = r"D:\code\test"
    
    with open(os.path.join(data_file_path_test, '特殊缺失值.json'), encoding="utf-8") as f:  # 类别型标签_itg
        cat_dict = json.load(f)  # 将文件中的 JSON 数据加载并解析成 Python 对象  字典值
        print(cat_dict)
    

    运行结果

    {'curr_hold_amt_mom': -2, 'curr_hold_amt_yoy': -2}
    

    五、pandas横表转竖表

    最后这段代码的主要作用是将数据从横表转换为竖表,这样做是为了在处理完客户标签后,以竖表的方式更清晰地展示数据。

    import pandas as pd
    
    
    def dataframe_transfer(df, tag_nm, key_list=['cust_no', 'crt_dt', 'cust_crm_no'], var_nm='tag_cd',
                           value_nm='tag_val_cd'):
        '''
        该函数用于将标签横表转换为竖表,其中:
        df          : dataframe,传入待处理的dateframe
        tag_nm      :list类型,代表标签名称
        key_list   :list类型,不做处理的关键字段列表,默认为['cust_no','crt_dt','cust_crm_no']
        var_nm      :str类型,转化后生成的新标签列名称,默认为'tag_cd'
        value_nm    : str类型,转化后生成的新标签值列名称,默认为'tag_val_cd'
    
        return df:传入的df后面新增处理完的新标签
        '''
        col = []
        col.extend(key_list)
        col.extend(tag_nm)
        ddf = df.loc[:, col].copy()
        ddf.loc[:, tag_nm] = ddf.loc[:, tag_nm].astype(str)
        ddf = pd.melt(ddf, id_vars=key_list, value_vars=tag_nm, var_name=var_nm, value_name=value_nm)
        return ddf
    
    
    # 创建测试数据
    data = {
        'cust_no': [1, 2, 3, 4],
        'crt_dt': ['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'],
        'cust_crm_no': ['CRM001', 'CRM002', 'CRM003', 'CRM004'],
        'tag1': ['A', 'B', 'C', 'D'],
        'tag2': ['X', 'Y', 'Z', 'W']
    }
    
    df = pd.DataFrame(data)
    
    # 显示原始数据
    print("原始数据:")
    print(df)
    
    # 调用函数进行转换
    tag_nm = ['tag1', 'tag2']
    result_df = dataframe_transfer(df, tag_nm)
    
    # 打印转换后的结果
    print("\n转换后的结果:")
    print(result_df)
    

    运行结果

    原始数据:
       cust_no      crt_dt cust_crm_no tag1 tag2
    0        1  2024-01-01      CRM001    A    X
    1        2  2024-01-02      CRM002    B    Y
    2        3  2024-01-03      CRM003    C    Z
    3        4  2024-01-04      CRM004    D    W
    
    转换后的结果:
       cust_no      crt_dt cust_crm_no tag_cd tag_val_cd
    0        1  2024-01-01      CRM001   tag1          A
    1        2  2024-01-02      CRM002   tag1          B
    2        3  2024-01-03      CRM003   tag1          C
    3        4  2024-01-04      CRM004   tag1          D
    4        1  2024-01-01      CRM001   tag2          X
    5        2  2024-01-02      CRM002   tag2          Y
    6        3  2024-01-03      CRM003   tag2          Z
    7        4  2024-01-04      CRM004   tag2          W
    

    今天的讲解就进入尾声了,本篇介绍了如何利用Pandas将指标数据巧妙地转化为标签。这只是Pandas在数据处理中的一个简单应用场景,而Pandas在Python数据分析和数据科学领域的功能远不止于此。希望大家能够保持对Python的探索和学习热情,继续深入了解和应用Pandas,共同在数据科学领域不断进步。

    相关文章

      网友评论

          本文标题:洞悉客户心声:Pandas标签帮你透视客户,标签化营销如虎添翼

          本文链接:https://www.haomeiwen.com/subject/srmfxjtx.html