目录

  • 一、标准化数据
  • (一)离差标准化数据
  • (二)标准差标准化数据
  • (三)小数定标标准化数据
  • (四)三种标准化的特点:
  • 二、转换数据
  • (一)哑变量处理类别数据
  • (二)离散化
  • 三、案例(坏账率分析案例)


一、标准化数据

(一)离差标准化数据

离差表转化是对原始数据的一种线性变换,结果是将原始的数据映射到[0,1]区间之间, 转换公式为:

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_标准差

import pandas as pd
import numpy as np


# 剔除数据量级的影响,减小运算量  ---数据标准化

"""
     标准化
     1、离差标准化
         对数据的线性变化,将数据映射到[0,1]范围
         new_x = (x - min) /(max -min)
         min 表示该列最小值, max表示该列的最大值
     
     特点:
         (1)转化之后,大数据还是较大的,小数据还是较小的
         (2)当数据为最小值,转化为的数据为0
         (3)极差过大--->分母过大--->整体的数据偏小 ---之间差异不太明显
         (4) 如果数据中的某个数值很大,分母很大,--整体趋向于0
         标准化之后,要记录下,最小值、最大值--->计算原始数据
         如果出现新的数据,不在之前记录[min,max]之间---转化之后,>1或者<0的情况
"""

# 实现离差标准化
def min_max_sca(data):
    """
    离差标准化
    :param data: series/dataframe
    :return: 标准化之后的数据
    """
    data = (data - data.min()) / (data.max() - data.min())

    return data


# 加载detail数据
detail = pd.read_excel('./meal_order_detail.xlsx')
print(detail.loc[:, 'amounts'])
print('*' * 100)


# 对amounts单价列进行离差标准化
# detail.loc[:, 'amounts'] = min_max_sca(detail.loc[:, 'amounts'])
# print(detail.loc[:, 'amounts'])

# 使用transform来进行使用自定义函数对数据进行离差标准化(推荐)
# detail.loc[:,'amounts'] = detail.loc[:,'amounts'].transform(min_max_sca)
# print("离差标准化后的结果",detail.loc[:, 'amounts'])

(二)标准差标准化数据

标准差标准化也叫零均值标准化或分数标准化,是当前使用最广泛的数据标准化方法。 经过该方法处理后的均值为 0,标准差为 1,转化公式为:

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_数据_02

import pandas as pd
import numpy as np

"""
     2、标准差标准化
         目前使用最为广泛的一种标准化数据的方式
         将数据转化为均值为0,标准差为的1 数据--->将数据转化为服从标准正态分布的数据
         new_x = (x - mean) / std
     
     特点:
         (1)标准化之后的数据不仅局限于[0,1],可以为正的,也可以为负的,总之,均值为0,标准差为1
         (2)不会影响数据的整体分布,大的值转化之后还是较大的,小的值转化之后还是较小的
"""

def stand_sca(data):
    """
    标准差标准化数据
    :param data: series/datafrmae
    :return: 标准化之后的数据
    """
    data = (data - data.mean()) / data.std()

    return data



# # 对amounts列进行标准差标准化的转化
detail.loc[:, 'amounts'] = detail.loc[:, 'amounts'].transform(stand_sca)
print("标准差标准化后的结果",detail.loc[:,'amounts'])

# # 验证
# print('标准差标准化转化之后的结果为:\n',detail.loc[:,'amounts'].mean())
# print('标准差标准化转化之后的结果为:\n',detail.loc[:,'amounts'].std())

(三)小数定标标准化数据

通过移动数据的小数位数,将数据映射到区间[-1,1]之间,移动的小数位数取决于数据 绝对值的最大值。转化公式如下:

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_标准差_03

import pandas as pd
import numpy as np

"""
     3、小数定标标准化
         通过移动小数点的位置来进行数据转化,将数据转化为[-1,1]之间
         小数点移动的位数 取决于该列数据绝对值的最大值
         new_x = x / 10 ^ k
         k ---> log10(|x|.max()) 向上取整 ---转化int
         假设存在一列数据 --->该列数据 [-99,99]之间
             a、该列数据取绝对值---[0,99]
             b、取绝对值的最大值---99
             c、对99进行取log10对数 --->(1,2)小数
             d、对 (1,2)小数 ---向上取整 ---> 2.0
             e、转化为int ---> 2  --->k = 2
             f、根据公式 分母 =100,--->小数点向左移动2位 ---->[-0.99,0.99]之间
         
     特点:
         (1)将数据映射到[-1,1]区间
         (2)数据的分布不会变化,大的数据转化之后还是较大的,小的数据转化之后还是小的
"""

# 实现小数定标标准化
def desc_sca(data):
    """
    小数定标标准化
    :param data: series/dataframe
    :return: 标准化之后的数据
    """
    # abs() --取绝对值
    # np.ceil --向上取整
    data = data / (10 ** (int(np.ceil(np.log10(data.abs().max())))))

    return data

# 利用小数定标标准化来标准化 amounts列数据
detail.loc[:,'amounts'] = detail.loc[:,'amounts'].transform(desc_sca)
print('小数定标标准化之后的结果:\n',detail.loc[:,'amounts'])
# 最大值:178 --->挪小数-->3位

# 离差标准化、小数定标标准化--容易受到异常值的影响
# 标准差标准化 ---异常值--对均值的影响 要比异常值 对具体某个数的影响要小的多

(四)三种标准化的特点:

离差标准化方法简单,便于理解,标准化后的数据限定在[0,1]区间内;

标准差标准化收到数据分布的影响较小;

小数定标准化方法的适用范围广,并且受到数据分布的影响较小,相比较于前面两种方法而言该方法适用程度适中。

二、转换数据

(一)哑变量处理类别数据

数据分析模型中有相当一部分的算法模型都要求输入的特征为数值型,但实际数据中特征的类型不一定只有数值型,还会存在相当一部分的类别型,这部分的特征需要经过哑变量处理才可以放入模型之中。

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_python_04


Python 中可以利用 Pandas 库中的 get_dummies 函数对类别型特征进行哑变量处理。

pandas.get_dummies(data,prefix=None,prefix_sep='_',dummy_na=False,columns=None, sparse=False,drop_first=False)

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_标准差_05

import pandas as pd
import numpy as np

# 将特定的数据 转化为 可以使用的数据---数据变换

# 两种情况:
# 1、将非数值型数据 ----->数值型(哑变量转化)

# 创建一个df
# df = pd.DataFrame(
#     data={
#         '城市': ['北京', '上海', '西安', '郑州', '石家庄', '重庆', '成都']
#     })
# print('df:\n', df)
# print('df的类型:\n', type(df))

# 城市数据 可能后续 需要计算
# # 将城市数据转化为 数值型 ---哑变量转化
# data :需要转化的数据
# prefix:转化之后列名称的前缀
# prefix_sep: 转化之后前缀与数据的连接符
# city_data = pd.get_dummies(data=df.loc[:, '城市'],
#                            prefix='city',
#                            prefix_sep='_'
#                            )
# print('转化之后的结果:\n', city_data)

(二)离散化

某些模型算法,要求数据是离散的,此时就需要将连续型特征(数值型)变换成离散型特征(类别型)。

连续特征的离散化就是在数据的取值范围内设定若干个离散的划分点,将取值范围划分为一些离散化的区间,最后用不同的符号或整数值代表落在每个子区间中的数据值。

因此离散化涉及两个子任务,即确定分类数以及如何将连续型数据映射到这些类别型数据上。

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_python_06


代码实现:

import pandas as pd
import numpy as np

# 2、将连续的小数数据---->区间数据(类别数据、离散数据) ---(离散化)
# 将连续的小数数据---拆分成不同的区间
# cut --拆分
# detail ---amounts单价数据 ---可以理解为连续的小数数据
# 将amounts进行拆分---离散化
# 加载detail数据
detail = pd.read_excel('./meal_order_detail.xlsx')
print('detail:\n', detail)
print('detail的列索引:\n', detail.columns)

# x : 需要离散化的数据
# bins : 分组的组数,分组节点
# include_lowest :默认为False ,如果让系统默认拆分,包含最小值
# 如果自定义划分分组节点---不包含最小值,需要将include_lowest =True
# res = pd.cut(x=detail.loc[:, 'amounts'],
#              bins=5,
#              include_lowest=True)
# # 原来具体的数值---->各个区间来代替原来的具体的值
# print('res:\n', res)
# print('*' * 100)
#
# # 可以同values_counts来查看分组之后,各个区间内的数量
# print(pd.value_counts(res))


# 自定义分组
# (1) 自己直接指定分组节点
# bins = [1, 40, 80, 120, 160, 180]
# res = pd.cut(x=detail.loc[:, 'amounts'],
#              bins=bins,
#              include_lowest=True)
# print('res:\n',res)
# print('*' * 100)
# # 可以同values_counts来查看分组之后,各个区间内的数量
# print(pd.value_counts(res))


# # (2) 等宽分组
# # a、确定分组组数
# group_num = 5
# # b、确定间距
# # 确定最大值
# max_amounts = detail.loc[:, 'amounts'].max()
# # 确定最小值
# min_amounts = detail.loc[:, 'amounts'].min()
# # 确定间距
# width = int(np.ceil((max_amounts - min_amounts) / group_num))
# # c、确定分组节点
# bins = np.arange(min_amounts, max_amounts + width, width)
#
# res = pd.cut(x=detail.loc[:, 'amounts'],
#              bins=bins,
#              include_lowest=True)
# print('res:\n', res)
# print('*' * 100)
# # 可以同values_counts来查看分组之后,各个区间内的数量
# print(pd.value_counts(res))



# (3) 等频分组
# 因为在分组的时候,使用等宽分组的时候,可能出现某个区间内无数据
# 使用等频分组--->在各个区间内的数量基本一致
# 分位数计算---quantile
# a、确定分组组数
group_num = 5
# b、确定bins
bins = detail.loc[:, 'amounts'].quantile(q=np.arange(0, 1 + 1 / group_num, 1 / group_num))

print('bins:\n', bins)

res = pd.cut(x=detail.loc[:, 'amounts'],
             bins=bins,
             include_lowest=True)
print('res:\n', res)
print('*' * 100)
# 可以同values_counts来查看分组之后,各个区间内的数量
print(pd.value_counts(res))

三、案例(坏账率分析案例)

银行坏账是指银行无法收回或收回的可能性极小的应收款项。由于发生坏账而产生的损失,称为坏账损失。

由于市场经济的极大的不确定性,银行的应收账款很可能最终不能够全部收回,即可能发生部分或者全部的坏账。

为此大部分银行业在对社会人员进行贷款的时候进行评估,以确保贷款能够完全被收回,以下为某银行对该行部分贷款用户的统计信息,请利用该信息,为该银行的贷款进行一个预估,查看坏账的原因都来自于哪些方面?

python为什么数据标准化不能使用一个变量了呢 pandas 数据标准化_大数据_07


注意:其中好坏用户中的 0 代表信用好用户,1 代表信用差用户。

针对以上统计信息,可以可以提出问题:

(1) 是否收入越高的人坏账率越高?

(2)计算年龄和坏账率有什么关系?

(3)计算家庭人口数量和坏账率有什么关系?

代码实现:

import pandas as pd
import numpy as np

# 加载数据
data = pd.read_csv('./loan.csv', encoding='ansi')
print('data:\n', data)
print('*' * 100)

# 好用户 用0 表示
# 坏用户 用1 表示

# [150000 rows x 6 columns]
# (1) 是否收入越高的人坏账率越高?
# 研究---收入--好坏用户数量
# 按照月收入进行分组,统计各个月收入区间内的 坏用户的数量
# 先检测缺失值
res_null = pd.isnull(data).sum()
print('缺失值检测结果:\n', res_null)
# 删除、填充、插值 ---填充月收入的缺失值
# 使用众数 来填充
# a、计算众数
mode = data.loc[:, '月收入'].mode()[0]
print('月收入的mode\n', mode)
# b、填充
data.loc[:, '月收入'].fillna(value=mode, inplace=True)
print('填充完月收入的缺失值之后再检测:\n')
print('检测结果为:\n', pd.isnull(data).sum())

# 剔除 月收入 <= 0 的数据
# 自定义 业务法 进行异常值剔除
# bool数组
data = data.loc[data.loc[:, '月收入'] > 0, :]
print('月收入异常值剔除的结果:\n', data.shape)
# 月收入是连续的小数的值,如果直接分组,会发现每个值都是一组
# 对 月收入进行分组 ---离散化
# 默认分组 ---结果--得不到结论
# data.loc[:, '月收入'] = pd.cut(x=data.loc[:, '月收入'],
#                             bins=5)

# # 等宽分组  ---效果也不好
# # a、确定分组组数
# group_num = 5
# # b、确定间距
# # 确定最大值
# max_salary = data.loc[:, '月收入'].max()
# # 确定最小值
# min_salary = data.loc[:, '月收入'].min()
# # 确定间距
# width = int(np.ceil((max_salary - min_salary) / group_num))
# # c、确定分组节点
# bins = np.arange(min_salary, max_salary + width, width)
#
# # 分组
# data.loc[:, '月收入'] = pd.cut(x=data.loc[:, '月收入'],
#                             bins=bins,
#                             include_lowest=True)

# 等频分组 ---效果不是特别明显
# # 等频分组
# # 因为在分组的时候,使用等宽分组的时候,可能出现某个区间内无数据
# # 使用等频分组--->在各个区间内的数量基本一致
# # 分位数计算---quantile
# # a、确定分组组数
# group_num = 5
# # b、确定bins
# bins = data.loc[:, '月收入'].quantile(q=np.arange(0, 1 + 1 / group_num, 1 / group_num))
#
# print('bins:\n', bins)
#
# # 分组
# data.loc[:, '月收入'] = pd.cut(x=data.loc[:, '月收入'],
#                             bins=bins,
#                             include_lowest=True)
# # 获取 月收入的最大值、最小值
# print('月收入最大值:\n', data.loc[:, '月收入'].max())
# print('月收入最小值:\n', data.loc[:, '月收入'].min())


# # 自定义分组
bins = [1, 5000, 10000, 200000, 350000]
# 分组
data.loc[:, '月收入'] = pd.cut(x=data.loc[:, '月收入'],
                            bins=bins,
                            include_lowest=True)
#
# 月收入---区间数据---category型
# 按照月收入分组,统计各个组内的坏用户的数量
res = data.groupby(by='月收入')['好坏客户'].sum()
print('统计各个组内的坏用户的数量:\n', res)
#  月收入 越高---坏账人数越少



# # (2)计算年龄和坏账率有什么关系?
# # 获取 年龄的 最小值、最大值
# print('年龄最大值:\n', data.loc[:, '年龄'].max())
# print('年龄最小值:\n', data.loc[:, '年龄'].min())
#
# # 如果认为不满 12个月的小孩 都是0岁 ---正常的 ---压岁钱
# # 18岁以前,银行不予以贷款  -思路
#
# # 认为所有的年龄 都是正常的
# # 各个年龄段的 坏账用户数量
# # 先得对年龄进行划分区间 ---再去进行按照年龄进行分组,统计好坏用户的数量
# # data.loc[:,'年龄'] = pd.cut(x=data.loc[:, '年龄'], bins=5)
# # 还要去尝试 等宽分组、等频分组---效果不好
#
# bins = [0, 20, 40, 60, 80, 110]
# data.loc[:, '年龄'] = pd.cut(x=data.loc[:, '年龄'], bins=bins, include_lowest=True)
#
# res = data.groupby(by='年龄')['好坏客户'].sum()
#
# # 计算各个年龄段的总人数
# res_all = data.groupby(by='年龄')['好坏客户'].count()
#
# # 主要是:所有数据统计的时候,大部分都是中年人,不能以人数来定论---可以讨论各个年龄段,坏账人占比
# print('结果为:\n', res / res_all)
#
# # --->随着年龄的增加 ---坏账人口 减小的



# (3)计算家庭人口数量和坏账率有什么关系?
# 删除掉  家庭人口数量  缺失的行
data.dropna(axis=0, how='any', inplace=True)
print('删除家属数量缺失的结果:\n', data.shape)

# 计算不同家属数量--坏账人数(坏账占比)
print('家属数量的最大值:\n', data.loc[:, '家属数量'].max())
print('家属数量的最小值:\n', data.loc[:, '家属数量'].min())

# 可以认为 家属数量 也是连续小数
# 先进行 家属数量 分组
# data.loc[:, '家属数量'] = pd.cut(x=data.loc[:, '家属数量'],
#                              bins=5)

# 等宽、等频会出现问题
bins = [0, 2, 6, 20]
# 自定义分组
data.loc[:, '家属数量'] = pd.cut(x=data.loc[:, '家属数量'],
                             bins=bins)

# 计算不同家属数量 的情况下 坏用户的人数
res = data.groupby(by='家属数量')['好坏客户'].sum()
res_all = data.groupby(by='家属数量')['好坏客户'].count()
print('人口数量和坏账率的关系为:\n', res / res_all)

#  随着家属人口数量的增加,坏账会相对下降 ---关系不是太强

# 可以再去尝试 ---占比饼图 --从饼图中得到结论