Python Data Science: Data Cleaning

Key principles of data cleaning

Posted by Mengran on August 19, 2021

Data Cleaning

Data cleaning is the process of ensuring that your data is correct, consistent and usable.

Principles of Data Cleaning

数据清洗规则总结为以下 4 个关键点,统一起来叫“完全合一”:

  • 完整性:单条数据是否存在空值,统计的字段是否完善。
  • 全面性:观察某一列的全部数值,比如在 Excel 表中,我们选中一列,可以看到该列的平均值、最大值、最小值。我们可以通过常识来判断该列是否有问题,比如:数据定义、单位标识、数值本身。
  • 合法性:数据的类型、内容、大小的合法性。比如数据中存在非 ASCII 字符,性别存在了未知,年龄超过了 150 岁等。
  • 唯一性:数据是否存在重复记录,因为数据通常来自不同渠道的汇总,重复的情况是常见的。行数据、列数据都需要是唯一的,比如一个人不能重复记录多次,且一个人的体重也不能在列指标中重复记录多次。

Using Pandas to clean data

Python 的 Pandas 工具基于 NumPy 的工具,专门为解决数据分析任务而创建。Pandas 纳入了大量库,可以利用这些库高效地进行数据清理工作。

Three principles:

  • Data Completeness
  • Data Comprehension

1.Data Completeness

Data Completeness includes cleaning missing values and NaN.

Missing Data 缺失值

在数据中有些年龄、体重数值是缺失的,这往往是因为数据量较大,在过程中,有些数值没有采集到。通常我们可以采用以下三种方法:

  • Delete 删除:删除数据缺失的记录;
  • Average 均值:使用当前列的均值;
  • Use highest frequency data 高频:使用当前列出现频率最高的数据。

Example 1: Fill missing data in column['Age'] with its average age.

if you want it to modify the df inplace, you have to explicitly specify

inplace=True

df['age'].fillna(df['age'].mean(), inplace=True)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd
from pandas import DataFrame
import numpy as np

df = DataFrame({'name':['ZhangFei', 'GuanYu', 'a', 'b', 'c'], 'age': [23, 19, np.nan, 25, 44]})

df['age'].fillna(df['age'].mean(), inplace=True)

print(df)

>>>
       name    age
0  ZhangFei  23.00
1    GuanYu  19.00
2         a  27.75
3         b  25.00
4         c  44.00

Example 2: Fill missing data with highly frequent value

如果我们用最高频的数据进行填充,可以先通过 value_counts 获取 Age 字段最高频次 age_maxf,然后再对 Age 字段中缺失的数据用 age_maxf 进行填充:

age_maxf = df['age'].value_counts().index[0]

df['age'].fillna(age_maxf, inplace=True)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import pandas as pd
from pandas import DataFrame
``
df = DataFrame({'name':['ZhangFei', 'GuanYu', 'a', 'b', 'c'], 'age': [23, 25, np.nan, 25, 44]})

age_maxf = df['age'].value_counts().index[0]
df['age'].fillna(age_maxf, inplace=True)

print(df)

>>>
       name   age
0  ZhangFei  23.0
1    GuanYu  25.0
2         a  25.0
3         b  25.0
4         c  44.0

Empty rows 空行

我们发现数据中有一个空行,除了 index 之外,全部的值都是 NaN

Pandasread_csv() 并没有可选参数来忽略空行,这样,我们就需要在数据被读入之后再使用 dropna() 进行处理,删除空行。

Drop the rows where all elements are missing:

df.dropna(how='all',inplace=True)

2.Data Comprehension

Data Comprehension, especially units/formats are not consistent.

观察 weight 列的数值,我们能发现 weight 列的单位不统一。

有的单位是千克(kgs),有的单位是磅(lbs)。

这里使用千克作为统一的度量单位,将磅(lbs)转化为千克(kgs):

1
2
3
4
5
6
7
8
9
10
11
# 获取 weight 数据列中单位为 lbs 的数据
rows_with_lbs = df['weight'].str.contains('lbs').fillna(False)
print df[rows_with_lbs]

# 将 lbs转换为 kgs, 2.2lbs=1kgs
for i,lbs_row in df[rows_with_lbs].iterrows():

  # 截取从头开始到倒数第三个字符之前,即去掉lbs。
  weight = int(float(lbs_row['weight'][:-3])/2.2)
  df.at[i,'weight'] = '{}kgs'.format(weight) 
  

3.Data Rationality

我们可以看到在数据集中 FirstnameLastname 有一些非 ASCII 的字符。

我们可以采用删除或者替换的方式来解决非 ASCII 问题,这里我们使用删除方法:

1
2
3
# 删除非 ASCII 字符
df['first_name'].replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True)
df['last_name'].replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True)

4.Data Uniqueness

Uniqueness – points out that there should be no data duplicates reported. Each data record should be unique, otherwise the risk of accessing outdated information increases.

Multiple Parameters in one column

Name includes two parameters:

  • Firstname
  • Lastname

在数据中不难发现,姓名列(Name)包含了两个参数 FirstnameLastname

为了达到数据整洁目的,我们将 Name 列拆分成 FirstnameLastname 两个字段。

我们使用 Pythonsplit 方法,str.split(expand=True),将列表拆成新的列,再将原来的 Name 列删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 切分名字,删除源数据列
import numpy as np
import pandas as pd
from pandas import DataFrame


df = DataFrame({'name': ['Zhang Fei', 'Guan Yu', 'david lewis', 'baba pa', 'can xu'],
                'age': [23, 25, np.nan, 25, 44],
                'empty': [np.nan, None, None, None, None]})

df[['first_name','last_name']] = df['name'].str.split(' ', 1, expand=True)
df.drop('name',axis=1, inplace=True)
print(df)

>>>
    age  empty first_name last_name
0  23.0    NaN      Zhang       Fei
1  25.0    NaN       Guan        Yu
2   NaN    NaN      david     lewis
3  25.0    NaN       baba        pa
4  44.0    NaN        can        xu

Repetitive Data

我们校验一下数据中是否存在重复记录。

如果存在重复记录,就使用 Pandas 提供的 drop_duplicates() 来删除重复数据。

1
2
# 删除重复数据行
df.drop_duplicates(['first_name','last_name'],inplace=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import pandas as pd
from pandas import DataFrame


df = DataFrame({'name': ['Zhang Fei', 'Zhang Fei', 'david lewis', 'baba pa', 'can xu'],
                'age': [25, 25, np.nan, 25, 44],
                'empty': [np.nan, None, None, None, None]})

df[['first_name','last_name']] = df['name'].str.split(' ', 1, expand=True)
df.drop('name',axis=1, inplace=True)
df.drop_duplicates(['first_name', 'last_name'], inplace=True)
print(df)

>>>
    age  empty first_name last_name
0  25.0    NaN      Zhang       Fei
2   NaN    NaN      david     lewis
3  25.0    NaN       baba        pa
4  44.0    NaN        can        xu

Exercise:

  • 完整性:ounces 列数据中存在NAN
  • 全面性:food列数据中存在大小写不一致问题
  • 合法性:ounces列数据存在负值
  • 唯一性:food列数据存在重复 处理:求和合并 因为是食物,可以把同样名字的食物加起来合成一行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -*- coding: utf-8 -*
import pandas as pd
import numpy as np
from pandas import Series, DataFrame

df = pd.read_csv('./fooddata.csv')

# 把ounces 列中的NaN替换为平均值
df['ounces'].fillna(df['ounces'].mean(), inplace=True)

# 把food列中的大写字母全部转换为小写
df['food'] = df['food'].str.lower()

# 把ounces 列中的负数转化为正数
df['ounces']= df['ounces'].apply(lambda x: abs(x))

#删除food列中的重复值
df.drop_duplicates('food',inplace=True)

print (df)