zoukankan      html  css  js  c++  java
  • python 数据清洗

    前言

    1. 删除重复

    2. 异常值监测

    3. 替换

    4. 数据映射

    5. 数值变量类型化

    6. 创建哑变量


    统计师的Python日记【第7天:数据清洗(1)】

    前言

    根据我的Python学习计划:

    Numpy → Pandas → 掌握一些数据清洗、规整、合并等功能 → 掌握类似与SQL的聚合等数据管理功能 → 能够用Python进行统计建模、假设检验等分析技能 → 能用Python打印出100元钱 → 能用Python帮我洗衣服、做饭 → 能用Python给我生小猴子......

    上一篇的数据合并,以及本篇的数据清洗,都是非常非常实用的技能。我们用Python做数据分析,其实会有80%的功夫花在这些操作上面。我曾经去德国专门学过如何用做SAS数据清洗,数据清洗有一个专门的流程,涉及到数据缺失处理、变量值覆盖、日期时间数据、异常值、多选题数据处理、文本处理等等。日常项目中,可能随时用到这里面的某个技能,今天,就来学习一下Python的数据清洗吧!

    现在有一份心脏病患者的数据,经过问卷调查之后,最终录入数据如下:

    • Age:年龄

    • Areas:来自哪里,有A/B/C/D四个地区

    • ID:患者的唯一识别编号

    • Package:每天抽几包烟,缺失的为-9,代表不抽烟

    • SHabit:睡眠习惯,1-早睡早起;2-晚睡早起;3-早睡晚起;4-晚睡晚起

    为了学习方便,假设这里就这些变量吧。

    看完这个变量说明我不淡定了,这个数据存在很多问题啊!Age是年龄?158是什么鬼??还有6岁小孩,每天抽1包烟?ID是唯一编号吗?为什么有3个1号、2个5号、2个9号、2个10号?

    这个数据问题太多了,因此我要逐一来清洗一下,顺便学一下数据清洗方面的知识。

    1. 删除重复

    3个1号、2个5号、2个9号、2个10号。这是数据录入中经常出现的问题——重复录入了,所以首先我要把那么多占空间又没用的重复数据剔除。

    介绍两个方法:data.duplicated() 和 data.drop_duplicates(),前者标记出哪些是重复的(true),后者直接将重复删除。



    所以drop.duplicates直接就将重复值删除了,默认保留第一条。

    以上是按照“有两行数据,这两行数据的所有变量值都一样,这么这两行就算重复数据”,但有时候我们会只根据一个变量来剔除重复,比如值根据Areas这个变量,那么A/B/C/D四个地区只会保留第一条,传入take_last=True则保留最后一个:

    A/B/C/D每个地区值保留一条数据了。

    2. 异常值检测

    在第一步剔除重复值之后。得到了无重复数据的data_noDup:

    第二步,我想检测一下数据中有没有异常值。首先可以用 describe() 进行一个描述分析,在第五天的学习中(第5天:Pandas,露两手)已经学过如何对数据进行描述:

    有两个变量值得我们注意,一个是age,最大值158、最小值6,肯定有问题,另一个是package,最小值是-9,存在缺失。

    用 data[条件] 的方式可以看一下有多少age大于100、age小于10,、package为-9的:



    好了,检测完毕,现在来处理这些异常值。

    3. 替换

    我要把异常的年龄替换成缺失,把package等于-9的替换成0(换成0是因为,不抽烟其实也就是抽烟数量为0,这样还能少一些缺失值)。

    替换的方式有2种,字典,或者替换关系组成的数组:

    (1)data.replace([A, B], [A_R, B_R]),如果这里替换之后的值A_R和B_R是一样的,那么[A_R,B_R]直接是A_R就可以了

    (2)data.replace({A:A_R, B:B_R}),这是字典的方式。

    所以,这里想要将age的6、158替换成缺失,就应该为:

    data_noDup['Age'].replace([158, 6], np.nan)

    将package的-9替换成0:

    data_noDup['Package'].replace(-9, 0)

    替换之后的数据命名为data_noDup_rep:



    4. 数据映射

    接下来的一些处理,是为了变量能够更加便于分析,首先是要进行数据映射。什么是映射呢?以Areas为例,Areas取四个地区:A/B/C/D,这四个地区在分析的时候并没有什么意义,但A/B/C为城市,D为农村,这个很有意义,所以我要根据areas创建新变量CType:U-城市、R-农村,映射关系如下:

    方法就是写一个映射字典,把A/B/C变成U,把D变成R:

    areas_to_ctype={'A':'U','B':'U','C':'U','D':'R'}

    然后使用 map(映射字典) 去创建新变量CType:

    data_noDup_rep['CType']=data['Areas'].map(areas_to_ctype)

    其实用替换也可以,但是替换是在原列上替换,而映射自己可以新建一个变量。

    5. 数值变量类型化

    接下来还要处理的变量是年龄Age,需要分成四组,

    • 0:30岁以下,也就是0到30岁

    • 1:30-40岁

    • 2:40-50岁

    • 3:50岁以上,不妨设为50-100岁

    这个问题如果用映射MAP的话就麻烦了,每一个年龄都要写一个映射。使用 cut 函数来分割,就可以自己分割成几个组。

    1)首先要设置几个分割点:0、30、40、50、100:cutPoint=[0,30, 40, 50,80]

    2)接着,用 cut(data, cutPoint) 的格式对age按照cutPoint进行划分:pd.cut(data_noDup_rep['Age'],cutPoint)

    3)最后,将这个赋给新变量ageGroup:data_noDup_rep['ageGroup'] =pd.cut(data_noDup_rep['Age'],cutPoint)

    这样很不好看有木有?怎么把四个组分别用0、1、2、3来表示呢?

    设定一个组标签groupLabel=[0,1,2,3],指定 labels=groupLable 即可。

    data_noDup_rep['ageGroup'] =pd.cut(data_noDup_rep['Age'],cutPoint, labels=groupLabel)

    一个问题来了,依稀记得之前做过一个项目,样本量有7000,年龄分组是按照分位数来分的,那再python中能否实现?

    可以的,用 qcut(data, n) 就可以,按照分位数分n组,比如分2组,那么就按照中位数来分,分4组,就按照四分位数来分。对这个例子我分两组:

    data_noDup_rep['ageGroup'] =pd.qcut(data_noDup_rep['Age'],2)

    6. 创建哑变量

    哑变量一般用于两种情况:一是变量值是无序并列的,比如例子中的SHabit,四个选项1、2、3、4是并列的;另一种就是多选题,也需要生成哑变量。

    以本例中的SHabit(睡眠情况)为例,四个取值是并列的,没有顺序,因此我们要把这1个问题变成4个:

    SHabit(睡眠习惯,1-早睡早起;2-晚睡早起;3-早睡晚起;4-晚睡晚起)

    变成:

    SHabit_1:是否早睡早起?(0-否,1-是)

    SHabit_2:是否晚睡早起?(0-否,1-是)

    SHabit_3:是否早睡晚起?(0-否,1-是)

    SHabit_4:是否晚睡晚起?(0-否,1-是)

    使用 get.dummies( data[‘SHabit’] )  就可以直接搞定:

    生成了四个变量。要把它合并入原数据data_noDup_rep中去,只要用 merge 就可以了(上一文刚刚介绍过数据的合并,戳复习→第6天:数据合并

    data_noDup_rep_dum =pd.merge(data_noDup_rep, pd.get_dummies(data_noDup_rep['SHabit']),right_index=True, left_index=True)

    (注:因为合并键值是索引,因此要用right_index=True和left_index=True)

    一个问题:变量名1、2、3、4太丑了!

    可以在get_dummies函数中加 prefix=’’  选项为名字加一个前缀:

    data_noDup_rep_dum =pd.merge(data_noDup_rep, pd.get_dummies(data_noDup_rep['SHabit'], prefix='SHabit' ), right_index=True, left_index=True)

    变量比较多,所以换行显示了。还有一种情况,如果SHabit是多选呢?每个人的睡眠习惯不止一种,像这样:

    这样的多选题数据,在分析中肯定一点用没有,处理的方法也是生成哑变量,如何生成?将在【第8天:数据清洗(2)文本分析】中学习,除此之外,还要学习如何进行分列处理、如何处理文本数据中的空白,如何使用正则表达式。

    前言

    1. 去除空白

    2. 分列

    3. 把多选题的文本处理成哑变量

    4. 下集预告


    统计师的Python日记【第8天:数据清洗(2)文本处理】

    前言

    根据我的Python学习计划:

    Numpy → Pandas → 掌握一些数据清洗、规整、合并等功能 → 掌握类似与SQL的聚合等数据管理功能 → 能够用Python进行统计建模、假设检验等分析技能 → 能用Python打印出100元钱 → 能用Python帮我洗衣服、做饭 → 能用Python给我生小猴子......

    从第6天开始入手学习了一些非常实用的Python数据处理技能,昨天(第7天)学习了数据的清洗,用的例子是一份心脏病病人的问卷数据:

    为了方便,假设只有这些变量:

    • Age:年龄

    • Areas:来自哪里,有A/B/C/D四个地区

    • ID:患者的唯一识别编号

    • Package:每天抽几包烟,缺失的为-9,代表不抽烟

    • SHabit:睡眠习惯,1-早睡早起;2-晚睡早起;3-早睡晚起;4-晚睡晚起

    我把这份数据中存在的问题一一处理了,包括:

    • 重复值删除

    ID为1的出现了三次、ID为5的出现了两次、ID为9和10的都分别出现了两次。

    • 异常值检测

    年龄还有158岁的、还有6岁的并且已经开始吸烟。

    • 替换

    将年龄异常的替换成缺失,将抽烟为-9的替换成0(不抽烟就是抽烟数量为0,替换之后既合理又可以减少缺失值)

    • 数据映射

    将Areas四个地区分别映射成农村(R)和城市(U)。

    • 数值变量类型化

    将年龄再进行一个分组,从连续变量变成类型变量。

    • 创建哑变量

    SHabit睡眠习惯的4个取值是无序并列的,这种情况在分析的时候要变成哑变量

    也留了一个问题:如果SHabit是多选呢?像这样:

    这个就更要创建哑变量了,就是把一个问题分成四个。但是用第7天的函数get_dummies()是搞不定的,这属于文本格式。

    好吧,那么今天就专门学习一下如何清洗乱七八糟的文本数据吧!

    1. 去除空白

    先等一会解决那个多选问题,从最基本的开始,如果我们的文本数据中,混入了很多空格该怎么办?比如Areas这个变量:


    这个问题不是钻牛角尖,因为之前经常遇到这样的情况,它会带来很多麻烦,比如,我想筛选出Areas为A的数据,用 if Areas == ’A‘,那些带空白的你就找不出来了。

    之前知道strip()这个函数可以解决单个字符串的问题:

    那么在Pandas中,是否可以直接用strip()?

    Series是不可以直接用strip()的,.map(str.strip) 可以帮我解决:

    data_noDup_rep_dum ['Areas'].map(str.strip)

    2. 分列

    很久之前,使用excel的岁月里,分列功能没少用过,有的数据是通过A:B的形式储存在一列中,分析的时候要把两列劈开。这里假设数据的ID与性别“粘”在一起了,格式为 ID:Gender

    分号前面的是ID,分号后面的代表性别,0为男性,1为女性。split() 可以帮我把它们劈开,如果是单个字符串,直接使用即可:

    但对于Pandas的数据结构,则要写循环:

    IDGender = pd.DataFrame((x.split(':') for x in data_noDup_rep_dum.ID))

    此句的意思就是将data_noDup_rep_dum中的ID列,逐行给劈开,结果为:

    注意看变量的列名为0、1,非常的丑,所以我决定再加上columns=指定一下列名

    IDGender = pd.DataFrame((x.split(':') for x in data_noDup_rep_dum.ID), columns=['ID_new','Gender'])

    然后再将这两列粘贴到原数据中去:

    pd.merge(IDGender, data_noDup_rep_dum, right_index=True,left_index=True)

    错了!!

    为什么?注意看劈开之后的IDGender数据,索引是0、1、2、3...9,从0到9,而原数据data_noDup_rep_dum的索引则是0、3、4、5......是因为前期我在做清洗的时候很多数据被砍掉了,所以没有1、2等。想索引一致,只需要在spilt(':')这个语句中加上index=data_noDup_rep_dum.index,换成原数据的索引即可。

    IDGender = pd.DataFrame((x.split(':') for xin data_noDup_rep_dum.ID) , index=data_noDup_rep_dum.index, columns=['ID_new','Gender'] )

    那么现在与原数据合并起来就行了:

    print pd.merge(IDGender, data_noDup_rep_dum, right_index=True, left_index=True)

    3. 把多选题的文本创建成哑变量

    正如开头所说的,如果SHabit是多选:

    这种变量在分析的时候完全没有用,必须要处理成哑变量,那么第7天中学的

    get_dummies是否可以呢?试一下吧:

    哑变量是生成了,但不是我想要的,我想要的就是四个选项变成的四个问题:1 2 3 4,当一个人多选了1和2,那么就在问题1下面和问题2下面赋值为1,其他赋值为0。

    str.contains() 可以帮我解决,它的作用是,在SHabit列中查找某个元素,当含有这个元素时,赋值为True,否则为False:

    data_noDup_rep_mul['SHabit_1'] = data_noDup_rep_mul['SHabit'].str.contains('1') 

    这个语句会生成一个新变量,SHabit_1,当原变量SHabit中包含1时,它为True,否则为False。

    用同样的方法生成SHabit_2、SHabit_3、SHabit_4:

    后期再处理成0/1就可以了。

    关于文本数据的处理,还有很多很多其他情况,比如下面这份文本数据:

    (01)1872-8756

    Body shop P1

    Book B13

    (05)9212-0098

    PD(05)9206-4571

    Shushuophone

    (12) 6753-5513

    None here

    PD(12)6434-4532

    P&DWashing

    ......(未显示完)

    假设这是一份产品名单,我现在只想把数字编码的数据,也即红色部分筛选出来,应该如何做?

  • 相关阅读:
    poj2388-Who's in the Middle(排序)
    poj1543-Perfect Cubes(暴力)
    poj1664-放苹果(递归)
    快速幂
    poj2389-Bull Math(大整数乘法)
    HDU2608-0 or 1(数论+找规律)
    poj1131-Octal Fractions(进制转换)
    [noip2011 d1t2]选择客栈
    [周记]8.7~8.16
    [noip2012d1t2] 国王游戏
  • 原文地址:https://www.cnblogs.com/think-and-do/p/6591523.html
Copyright © 2011-2022 走看看