python数据分析十六:pandas的面元划分和哑变量(get_dummies()、哑变量)

数学名词

离散化和面元划分 :就是分组,进行相应的计算

对于数据进行离散化和面元划分的前提条件是:连续变化的数据
例如下面是一组人的年龄数据,现在要按照年龄划分为不同年龄的4组(即把数据拆分为4个面元),
分别为“18到25”、“25到35”、“35到60”及“60以上。为了实现分组,需要使用pandas的cut函数:
pandas返回的是一个特殊的Categorical对象。你可以将其看作一组表示面元名称的字符串。实际上它含义一个表示不同分类名称的levels数组以

及一个为年龄数据进行标号的labels属性。, 以及各个阶段人的数量统计

哑变量
也叫虚拟变量,引入哑变量的目的是,将不能够定量处理的变量量化,如职业、性别对收入的影响,战争、自然灾害对GDP的影响,
季节对某些产品(如冷饮)销售的影响等等。
这种“量化”通常是通过引入“哑变量”来完成的。根据这些因素的属性类型,构造只取“0”或“1”的人工变量,通常称为哑变量,记为D。

举一个例子,假设变量“职业”的取值分别为:工人、农民、学生、企业职员、其他,5种选项,我们可以增加4个哑变量来代替“职业”这个变量,
分别为D1(1=工人/0=非工人)、D2(1=农民/0=非农民)、D3(1=学生/0=非学生)、D4(1=企业职员/0=非企业职员),
最后一个选项“其他”的信息已经包含在这4个变量中了,所以不需要再增加一个D5(1=其他/0=非其他)了。
这个过程就是引入哑变量的过程,其实在结合分析(conjoint analysis)中,就是利用哑变量来分析各个属性的效用值的。

# -*- coding: utf-8 -*-
import pandas as pd

from pandas import Series,DataFrame

import numpy as np
'''
离散化和面元划分 :就是分组,进行相应的计算

对于数据进行离散化和面元划分的前提条件是:连续变化的数据
例如下面是一组人的年龄数据,现在要按照年龄划分为不同年龄的4组(即把数据拆分为4个面元),
分别为“18到25”、“25到35”、“35到60”及“60以上。为了实现分组,需要使用pandas的cut函数:
pandas返回的是一个特殊的Categorical对象。你可以将其看作一组表示面元名称的字符串。实际上它含义一个表示不同分类名称的levels数组以

及一个为年龄数据进行标号的labels属性。, 以及各个阶段人的数量统计

数据挖掘中有些算法,特别是分类算法,只能在离散型数据上进行分析,然而大部分数据集常常是连续值和离散值并存的。因此,
为了使这类算法发挥作用,需要对数据集中连续型属性进行离散化操作。
那么,如何对连续型属性离散化呢?常见的有等宽分箱法,等频分箱法:

等宽分箱法的思想是,将数据均匀划分成n等份,每份的间距相等。

等频分箱法的思想是,将观察点均匀分成n等份,每份的观察点数相同。

在对数据离散化前,需要先处理异常点敏感问题,即我们需要首先设定一个阈值将异常数据移除。有两种思路:

1.设定阈值为90%,将数据从小到大排序,移除全部数据最小的5%和最大的5%数据

2.  设定阈值为90%,将数据从小到大排序,然后对所有数据求和,并计算每个数据占总和的比例,移除占比10%的数据

在这里,我们实现的方法是等宽分箱法,针对连续型数据集,代码比较简单基础,在此抛砖引玉,大家可以在代码的基础上增添自己需要的功能。
'''

ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

bins = [18, 25, 35, 60, 100]

cats = pd.cut(ages, bins)

#每一个数据所在的区间范围
#数据的长度
#Categories对象,包含了分组的数量以及不同分类的名称
print(cats)
# [(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
# Length: 12
# Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]

#为年龄数据进行标号,比如0就是(18,25)的标号
print(cats.codes)
# [0 0 0 1 0 0 2 1 3 2 2 1]

#不同面元先的数据的数量
print(pd.value_counts(cats))
# (18, 25]     5
# (35, 60]     3
# (25, 35]     3
# (60, 100]    1
# dtype: int64

# 还可以不指定面元的界限,直接传入一个整数参数,cut()会按照指定的数字,将元素划分为相应的几部分。
cats2=pd.cut(ages,5)
print(cats2)
# [(19.959, 28.2], (19.959, 28.2], (19.959, 28.2], (19.959, 28.2], (19.959, 28.2], ..., (28.2, 36.4], (52.8, 61.0], (44.6, 52.8], (36.4, 44.6], (28.2, 36.4]]
# Length: 12
# Categories (5, interval[float64]): [(19.959, 28.2] < (28.2, 36.4] < (36.4, 44.6] < (44.6, 52.8] <
#                                     (52.8, 61.0]]


#qcut会得到各个面元相同数量的数据点
data=np.random.randn(1000)#正态分布
cats3=pd.qcut(data,4)#按四分位数进行切割
print(pd.value_counts(cats3))

# (0.627, 3.299]       250
# (-0.0917, 0.627]     250
# (-0.748, -0.0917]    250
# (-3.593, -0.748]     250
# dtype: int64

'''
检测和过滤异常值
'''
data=DataFrame(np.random.randn(1000,4))#正态分布
print(data.describe())
#           0            1            2            3
# count  1000.000000  1000.000000  1000.000000  1000.000000
# mean     -0.033081     0.058738    -0.027423    -0.028144
# std       0.976585     1.005248     1.012929     1.017619
# min      -3.118296    -3.133514    -3.128117    -2.966178
# 25%      -0.683512    -0.611462    -0.683149    -0.664482
# 50%      -0.057522     0.033559    -0.016225    -0.051340
# 75%       0.638193     0.751441     0.692127     0.643136
# max       3.180026     3.027844     3.149158     3.694023

#找出大于3的值的行,any()用法
print(data[(np.abs(data)>3).any(1)])

#     0         1         2         3
# 126 -0.829001 -1.025659  3.160653 -0.149694
# 278  3.068706  2.759795 -0.973630  1.290398
# 298 -0.813094 -0.199006  0.877084 -3.051210
# 342  1.019925 -3.398058  1.281663 -0.924395
# 520  0.529010 -3.053272 -0.231004  0.163613
# 603  1.770662 -3.697815 -0.056443 -1.430715
# 613 -0.223834  0.899780  3.186809  0.363643
# 633 -0.247019 -4.399414  0.777015  0.449591
# 831 -0.662822  0.929492  0.637030 -3.116932

#对数据大于3的数值进行赋值
print(data[np.abs(data)>3])#data矩阵中绝对值大于3的显示小于3的显示为NaN (988 NaN NaN  3.047267 NaN)

#赋值
data[np.abs(data)>3]=np.sign(data)*3
print(data.describe())
#          0            1            2            3
# count  1000.000000  1000.000000  1000.000000  1000.000000
# mean     -0.003373    -0.013148    -0.026312    -0.035093
# std       0.986391     0.999095     1.004011     0.995485
# min      -3.000000    -3.000000    -3.000000    -3.000000
# 25%      -0.670433    -0.691231    -0.739701    -0.715927
# 50%       0.005562    -0.017495     0.010079    -0.025116
# 75%       0.678260     0.648360     0.652048     0.666474
# max       3.000000     2.880646     3.000000     2.698668


'''
排列,随机采样
'''

df=DataFrame(np.arange(20).reshape(5,4))
print(df)

#随机排列五个整数
sampler=np.random.permutation(5)
print(sampler)

#将sampler代替索引
print(df.take(sampler))
#   0   1   2   3
# 4  16  17  18  19
# 1   4   5   6   7
# 2   8   9  10  11
# 3  12  13  14  15
# 0   0   1   2   3
#随机获取前三个数
print(np.random.permutation(len(df))[:3])
# [0 1 3]
print(df.take(np.random.permutation(len(df))[:3]))
#  0  1   2   3
# 1  4  5   6   7
# 2  8  9  10  11
# 0  0  1   2   3


'''
哑变量
也叫虚拟变量,引入哑变量的目的是,将不能够定量处理的变量量化,如职业、性别对收入的影响,战争、自然灾害对GDP的影响,
季节对某些产品(如冷饮)销售的影响等等。
这种“量化”通常是通过引入“哑变量”来完成的。根据这些因素的属性类型,构造只取“0”或“1”的人工变量,通常称为哑变量,记为D。

举一个例子,假设变量“职业”的取值分别为:工人、农民、学生、企业职员、其他,5种选项,我们可以增加4个哑变量来代替“职业”这个变量,
分别为D1(1=工人/0=非工人)、D2(1=农民/0=非农民)、D3(1=学生/0=非学生)、D4(1=企业职员/0=非企业职员),
最后一个选项“其他”的信息已经包含在这4个变量中了,所以不需要再增加一个D5(1=其他/0=非其他)了。
这个过程就是引入哑变量的过程,其实在结合分析(conjoint analysis)中,就是利用哑变量来分析各个属性的效用值的。
在线性回归分析中引入哑变量的目的是,可以考察定性因素对因变量的影响,引入哑变量有两种方式:加法方式与乘法方式。

所谓加法方式是指,哑变量作为单独的自变量,有独立的系数,从几何意义上来讲,就是只改变回归直线的截距(constant),不改变斜率(B)

而乘法方式则正好相反,不改变截距,只改变斜率,因为哑变量在回归方程中不是作为一个独立的自变量,而是与其中某一个自变量相乘后作为一个自变量。
当然,也可以同时使用加法和乘法来引入哑变量,即同时改变截距和斜率。

由于哑变量的取值只有0和1,它起到的作用像是一个“开关”的作用,它可以屏蔽掉D=0的case,使之不进入分析,在spss软件中就是filter的作用。
我试验了一下,确实如此。

利用spss软件自带的data:<u>car.sav</u>,分析汽车的功率与100米加速时间的关系,将变量“filter_$”作为哑变量,我们分别run两次线性回归分析,
然后对比这2次的结果,来说明上面的想法。

第一次:将哑变量“filter_$”纳入到分析中,同时利用加法和乘法来引入,将“accel”作为因变量,“horse”、“filter_$”、“filter_$*horse”
作为自变量,进行线性回归分析;

第二次,利用变量“filter_$”进行筛选case,即不分析filter_$=0的case,同样将“accel”作为因变量,而只将“horse”作为自变量;
两次的线性回归结果分别为:

第一次:accel=20.495-0.049*horse-0.007*"filter_$*horse"+0.738*filter_$
第二次:accel=21.234-0.056*horse
当filter_$=1时,第一次计算的公式正好等于第二次机算的公式。
'''

#机器学习的转换方式:将分类变量转换为‘哑变量矩阵’,或者‘指标矩阵’;
df=DataFrame({'key':['b','b','a','c','a','b'],'data1':range(6)})
print(pd.get_dummies(df['key']))
#   a  b  c
# 0  0  1  0
# 1  0  1  0
# 2  1  0  0
# 3  0  0  1
# 4  1  0  0
# 5  0  1  0

#加上前缀,方便与其他数据结合
dummies=pd.get_dummies(df['key'],prefix='key')
df_with_dummy=df[['data1']].join(dummies)
print(df_with_dummy)
#   data1  key_a  key_b  key_c
# 0      0      0      1      0
# 1      1      0      1      0
# 2      2      1      0      0
# 3      3      0      0      1
# 4      4      1      0      0
# 5      5      0      1      0


'''
一个对统计应用有用的秘诀是:结合get_dummies和cut之类的离散化函数
'''
#正态分布是个数值
data=np.random.rand(10)
print(data)
# [0.01472476 0.21786879 0.80685543 0.33888554 0.28585739 0.03260082
#  0.86698275 0.86326211 0.36818782 0.04061498]

#设置面元
bins=[0,0.2,0.4,0.6,0.8,1]

#面元划分
print(pd.cut(data,bins))
# [0.09044921 0.34034557 0.16417927 0.45625488 0.05030981 0.26397673
#  0.93966121 0.17444847 0.19196159 0.48356917]
# [(0.0, 0.2], (0.2, 0.4], (0.0, 0.2], (0.4, 0.6], (0.0, 0.2], (0.2, 0.4], (0.8, 1.0], (0.0, 0.2], (0.0, 0.2], (0.4, 0.6]]
# Categories (5, interval[float64]): [(0.0, 0.2] < (0.2, 0.4] < (0.4, 0.6] < (0.6, 0.8] < (0.8, 1.0]]
#    (0.0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1.0]

#哑变量
print(pd.get_dummies(pd.cut(data,bins)))

#    (0.0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1.0]
# 0           1           0           0           0           0
# 1           0           1           0           0           0
# 2           1           0           0           0           0
# 3           0           0           1           0           0
# 4           1           0           0           0           0
# 5           0           1           0           0           0
# 6           0           0           0           0           1
# 7           1           0           0           0           0
# 8           1           0           0           0           0
# 9           0           0           1           0           0

又是美好的周一了,要加油了

每天进步一点点


猜你喜欢

转载自blog.csdn.net/qq_38788128/article/details/80796776