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)中,就是利用哑变量来分析各个属性的效用值的。
[/code]

```code
  # -*- 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

 

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

每天进步一点点

 

 

posted on 2021-07-07 15:37  BabyGo000  阅读(577)  评论(0)    收藏  举报