Python数据预处理

数据预处理包括数据的清洗、合并、重塑与转换,解决数据缺失、极端值、数据格式不统一等问题,本文通过介绍Pandas中专门用于进行数据预处理的函数及方法来进行数据预处理的标准流程。

0. 数据清洗

数据清洗是一项复杂的工作,该过程目的在于提高数据的质量,将脏数据清理干净,使数据具有完整性、唯一性、权威性、合法性、一致性等特点。Pandas常见的数据清洗操作有空值和缺失值的处理、重复值的处理、异常值的处理、统一数据格式等。

0.1 空值和缺失值的处理

空值一般表示数据未知、不适用或将在以后添加数据。缺失值是指数据集中某个或某些属性不完整主要原因有机械原因和人为原因,机械原因往往因为机器故障而人为原因可能由于人为隐瞒或录入失误而引起的。空值使用None表示,缺失值使用NaN表示,处理空值和缺失值通常有如下四个函数:

函数名 函数效果
isnull() 检查空值或缺失值
notnull() 检查空值或缺失值
dropna() 删除含有空值或缺失值的行或列
fillna() 填充空值或缺失值
0.1.1 isnull()notnull()

isnull()notnull()参数都是只有一个,表示检查空值对象,不同之处在于isnull()如果检测到NoneNaN则标记为Truenotnull()检测到NoneNaN则标记为False,例如:

1
2
3
4
5
6
7
8
import pandas as pd
from numpy import NaN
data = pd.DataFrame(
[[1, 2, 3, NaN], [2, 4, NaN, NaN], [NaN, NaN, NaN, NaN], [1, 2, 6, 7]],
columns=['A', 'B', 'C', 'D'])
print(data)
print(pd.isnull(data))
print(pd.notnull(data))

输出:

1
2
3
4
5
6
7
8
9
10
       A      B      C      D
0 False True False True
1 False False False True
2 False True False False
3 True False False True
A B C D
0 True False True False
1 True True True False
2 True False True True
3 False True True False
0.1.2 dropna()

该方法作用是删除含有空值或缺失值的行或列。

语法:
1
pandas.dropna(axis=0,how='any',thresh=None,subset=None,inplace=False)
  • axis:确定过滤行的行或列,默认为0。取值为0index时删除包含缺失值或空值的行;取值为1columns时删除包含缺失值或空值的列。
  • how:确定过滤的标准,默认为any,取值为any时,则存在NaNNone则删除该行或该列;取值为all时,则所有值都为NaNNone是才删除该行或该列。
  • thresh:表示有效数据的最小要求,如果传入了2,则是要求该行或该列至少有两个非NaNNone时将其保留。
  • subset:表示在特定子集中的寻找NaNNone
  • inplace:表示是否在原数据上操作默认为False,如果设为True则在原数据操作,如果设为False则修改原数据的副本返回新的数据。
实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pandas as pd
from numpy import NaN
data = pd.DataFrame(
[[1, 2, 3, NaN], [2, 4, NaN, NaN], [NaN, NaN, NaN, NaN], [1, 2, 6, 7]],
columns=['A', 'B', 'C', 'D'])

# 默认参数
print(data.dropna())

# 当一行/列全部都为NaN时删除该行/列
print("\n", data.dropna(how='all'))

# 直接对原始数据操作
data.dropna(inplace=True)
print("\n修改后的数据为:\n", data)

输出为:

1
2
3
4
5
6
7
8
9
10
11
     A    B    C    D
3 1.0 2.0 6.0 7.0

A B C D
0 1.0 2.0 3.0 NaN
1 2.0 4.0 NaN NaN
3 1.0 2.0 6.0 7.0

修改后的数据为:
A B C D
3 1.0 2.0 6.0 7.0
0.1.3 fillna()

该方法可实现空值或缺失值的填充。

语法:
1
pandas.fillna(value=None,methon=None,axis=None,inplace=False,limit=None,downcast=None,**kwargs)
  • value:用于填充的数值
  • methon:表示填充方式,默认为None另外支持一下取值
    • pad/ffill:将最后一个有效数据向后传播,也就是说用缺失值前面的一个值代替缺失值。
    • backfill/bfill:将最后一个有效值向前传播,也就是说用缺失值后面的一个值代替缺失值。
  • limit:可以连续填充的最大数据量,默认为None

注意:methodvalue参数不能同时使用。

实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
from numpy import NaN
data = pd.DataFrame(
[[1, 2, 3, NaN], [2, 4, NaN, NaN], [NaN, NaN, NaN, NaN], [1, 2, 6, 7]],
columns=['A', 'B', 'C', 'D'])

# 常数填充缺失值
print("用常数6填充缺失值:\n", data.fillna(value="6"))

# 向下填充缺失值(前向填充)
print("\n前向填充缺失值:\n", data.fillna(method="ffill"))

# 指定列填充缺失值
print("\n用常数10填充A列缺失值,用常数20填充B列缺失值:\n", data.fillna({'A': 10, 'B': 20}))

输出为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
用常数6填充缺失值:
A B C D
0 1 2 3 6
1 2 4 6 6
2 6 6 6 6
3 1 2 6 7

前向填充缺失值:
A B C D
0 1.0 2.0 3.0 NaN
1 2.0 4.0 3.0 NaN
2 2.0 4.0 3.0 NaN
3 1.0 2.0 6.0 7.0

用常数10填充A列缺失值,用常数20填充B列缺失值:
A B C D
0 1.0 2.0 3.0 NaN
1 2.0 4.0 NaN NaN
2 10.0 20.0 NaN NaN
3 1.0 2.0 6.0 7.0

0.2 重复值的处理

当数据中出现了重复值,大多数情况需要删除,例如下表中:

id name height sex
0 1 zz 160
1 2 yxy 160
2 3 zxy 170
3 4 wcc 170
4 4 wcc 170

显然索引为34的数据完全相同需要进行删除,对于重复值的处理,Pandas提供了以下两个函数:

函数 函数效果
duplicated() 标记是否有重复值
drop_duplicates() 删除重复值
0.2.1 duplicated()重复值标记函数
语法:
1
duplicated(subset=None,keep='first')
  • subset:用于识别重复的列标签或列标签序列,默认识别所有列标签。
  • keep:删除重复值并保留第一次出现的项,取值如下:
    • first:从前向后查找,除了第一次出现的项其余相同的均标记为重复。(默认值)
    • last:从后往前查找,除了最后一次出现的项其余相同的均标记为重复。
    • False:所有相同的标记均标记为重复。
实例:
1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
data = pd.DataFrame(
[[1, 'zz', 160, '男'], [2, 'yxy', 160, '男'], [3, 'zxy', 170, '女'],
[4, 'wcc', 170, '男'], [4, 'wcc', 170, '男']],
columns=['id', 'name', 'height', 'sex'])
data_p = pd.DataFrame([
data.duplicated(),
data.duplicated(keep="last"),
data.duplicated(['height'])
],
index=['无参数', '从后向前查找', '指定height列'])
print(data_p)

输出为:

1
2
3
4
               0      1      2      3      4
无参数 False False False False True
从后向前查找 False False False True False
指定height列 False True False True True
0.2.2 drop_duplicates()重复值删除函数
语法:
1
drop_duplicates(subset=None,keep="first",inplace=False)
实例:
1
2
3
4
5
6
import pandas as pd
data = pd.DataFrame(
[[1, 'zz', 160, '男'], [2, 'yxy', 160, '男'], [3, 'zxy', 170, '女'],
[4, 'wcc', 170, '男'], [4, 'wcc', 170, '男']],
columns=['id', 'name', 'height', 'sex'])
print(data.drop_duplicates())

输出为:

1
2
3
4
5
   id name  height sex
0 1 zz 160
1 2 yxy 160
2 3 zxy 170
3 4 wcc 170

0.3 异常值的处理

异常值指的是样本中的个别值,其数值明显偏离它所属样本的其余观测值:

异常值

如图,蓝色圈出来的点明显高于其他值。对于异常值的检测通常使用3σ原则(拉依达准则)箱型图检测。

0.3.1 基于3σ原则检测异常值

3σ原则又称拉依达原则,它是指假设一组检测数据中只含有随机误差,对其进行计算处理得到标准偏差,按一定概率确定一个区间,凡是超过这个区间的误差都是粗大误差,在此误差范围内的数据应予以剔除,在正态分布概率公式中,σ表示标准差,μ表示平均数,f(x)表示正态分布函数,表达式如下:

其中正态函数的分布函数图如下所示:

正态函数

  • 数值分布在(μ-σμ+σ)中的概率为68.27%
  • 数值分布在(μ-2σμ+2σ)中的概率为95.44%
  • 数值分布在(μ-3σμ+3σ)中的概率为99.70%

通过原则,可以定义如下函数:

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


# ser1表示传入DataFrame的某一列
def three_sigma(ser1):
# 求平均值
mean_value = ser1.mean()
# 求标准差
std_value = ser1.std()
# 位于(μ-3σ,μ+3σ)区间内的数据是正常的,不在该区间的数据是异常的
# ser1中的数值小于μ-3σ或大于μ+3σ均为异常值
# 一旦发现异常值就标注为True,否则标注为False
rule = (mean_value - 3 * std_value > ser1) | (mean_value + 3 * std_value < ser1)
# 返回异常值的位置索引
index = np.arange(ser1.shape[0])[rule]
# 获取异常数据
outrange = ser1.iloc[index]
return(outrange)

对于一组数据例如:1,2,3,4,5,1000,2,3,4,5,3,8,23,12,66,通过3σ原则来进行检测:

1
2
3
import pandas as pd
data = pd.DataFrame({"A": [1, 2, 3, 4, 5, 1000, 2, 3, 4, 5, 3, 8, 23, 12, 66]})
print(three_sigma(data['A']))

检测结果为:

1
2
5    1000
Name: A, dtype: int64
0.3.2 基于箱型图检测异常值

箱型图是一种用作显示一组数据分散情况的统计图,在箱型图中,异常值被定义为小于Ql-1.5IQR或大于Qu+1.5IQR的值。其中:

  • Ql称为下四分位数,表示全部观察值中有四分之一的数据取值比他小
  • Qu称为上四分位数,表示全部观察值中有四分之一的数据取值比他大
  • IQR称为四分位数的间距,是上四分位数Qu与下四分位数Ql之差,其间包含了全部观察值的一半。

箱型图

离散点表示的是异常值,上界表示除异常值以外数据中最大值;下界表示除异常值以外数据中最小值。

箱型图根据实际的数据进行绘制,对数据没有任何要求,箱型图判断异常值的标准是以四分位数和四分位距为基础。在Pandas中提供了一个boxplot()方法用于绘制箱型图:

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
import matplotlib.pyplot as plt

data = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [2, 3, 5, 2],
'C': [1, 4, 7, 4],
'D': [1, 5, 30, 3]
})

data.boxplot(column=['A', 'B', 'C', 'D'])
plt.show()

output

使用vscodepycharm进行可视化需要使用绘图库,如果需要直接显示可以通过使用Jupyter进行快速编辑,安装教程如下:

通过在Jupyter中输入:

1
2
3
4
5
6
7
8
import pandas as pd
data = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [2, 3, 5, 2],
'C': [1, 4, 7, 4],
'D': [1, 5, 30, 3]
})
data.boxplot(column=['A', 'B', 'C', 'D'])

即可达到实时显示的目的:

Jupyter

可以看出根据检出结果异常值被检测出,对于异常值,通常会使用以下四种方式处理异常值:

  • 删除异常值记录
  • 使用具体值进行替换,例如使用前后两个观测值的平均值进行修正
  • 不处理,直接在具有异常值的数据集中统计分析
  • 视为缺失值,利用缺失值的处理方法修正异常值

如果需要对异常值进行替换,可以使用replace()函数。

replace()

函数语法:

1
replace(to_replace=None,value=None,inplace=False,limit=None,regex=False,method='pad')
  • to_replace:表示查找被替换的方式
  • value:用来替换任何匹配to_replace的值,默认为None
  • limit:表示前向或后向填充的最大尺寸间隙
  • regex:接收布尔值或与to_replace相同的类型,默认为False,表示是否将to_replacevalue解释为正则表达式。

实例:

1
2
3
4
5
6
7
8
9
import pandas as pd
data = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [2, 3, 5, 2],
'C': [1, 4, 7, 4],
'D': [1, 5, 30, 3]
})
data.replace(to_replace=30, value=3, inplace=True)
print(data)

通过replace方法将data中的异常值30直接在原数据集中替换为3,注意,replace()方法中的to_replace参数的传入值也可为列表list

输出:

1
2
3
4
5
   A  B  C  D
0 1 2 1 1
1 2 3 4 5
2 3 5 7 3
3 4 2 4 3

0.4 更改数据类型

在处理数据时可能会遇到数据类型不一致的问题,对于数据类型的统一有如下三种方式:

  • 创建时指定数据类型
  • astype()方法
  • numberic()方法
0.4.1 创建时指定数据类型

使用dtype参数,例如:

1
2
3
import pandas as pd
data = pd.DataFrame({"A": ['1', '2', '3'], "B": ['4', '5', '6']}, dtype='int')
print(data.dtypes)

可以看出输入的对象是字符型,但是通过指定dtype最终的对象为int型。

1
2
3
A    int32
B int32
dtype: object
0.4.2 astype()方法
语法:
1
astype(dtype,copy=True,errors='raise',** kwargs)
  • dtype:数据类型
  • copy:是否建立副本,默认为True
  • errors:错误采取的处理方式,默认为raise(允许引发异常),还可以设置为ignore(抑制异常)
实例:
1
2
3
4
import pandas as pd
data = pd.DataFrame({"A": ['1.1', '2.2', '3'], "B": ['4', '5', '6']}, dtype='float')
print(data.dtypes)
print(data['A'].astype(dtype='int'))
输出:
1
2
3
4
5
6
7
A    float64
B float64
dtype: object
0 1
1 2
2 3
Name: A, dtype: int32
0.4.3 to_numeric()

astype()方法存在局限性,通过to_numeric()函数可以将传入参数转换为数值类型。

语法:
1
pandas.to_numeric(arg,error='raise',downcast=None)
  • arg:表示要转换的数据,可以是list,tuple,Series
  • errors:错误采取的处理方式

to_numeric函数无法直接操作DataFrame对象

实例:
1
2
3
import pandas as pd
data = pd.Series(['1', '1.2', '3'])
print(pd.to_numeric(data).dtypes)
输出:
1
float64

1. 数据合并

1.1 轴向堆叠数据

通过使用concat()函数可以沿着一条轴对多个对象进行堆叠,语法格式为:

1
pandas.concat(objs,axis=0,join='outer',join_axes=None,ignore_index=False,keys=None,levels=None,names=None,verify_integrity=False,sort=None,copy=True)
  • axis:表示连接的轴向,默认为0,还可以设置为1
  • join:表示连接方式,inner表示内连接,outer表示外连接,默认外连接
  • ignore_index:接收布尔值,默认为False,如果设置为True,则表示清除现有索引并重置索引值
  • keys:接收序列,表示添加最外层索引
  • levels:用于构建MultiIndex的特定级别(唯一)
  • names:在设置了keyslevel参数后,用于创建分层级别的名称
  • verify_integerity:检查新的连接轴是否包含重复值。接收布尔值,当设置为True时,如果有重复的轴将会抛出错误,默认为False
1.1.1 横向堆叠、纵向堆叠、内连接、外连接

堆叠方式

横向堆叠以为标准,相同名称的行保存为一行,而纵向堆叠以为标准,相同名称的列保存为一列;内连接是将两个DataFrame对象中都有的列或行进行堆叠连接,而外连接是将两个DataFrame对象所有的列和行进行堆叠连接。

横向堆叠默认不考虑列索引而纵向堆叠默认不考虑行索引,缺失值使用NaN填充

实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
data1 = pd.DataFrame(
{
"A": [0, 1, 2, 3],
"B": [4, 5, 6, 7],
"C": [8, 9, 10, 11]
}, dtype=int)
data2 = pd.DataFrame({
"B": [12, 13, 14],
"C": [15, 16, 17],
"D": [18, 19, 20]
},
dtype=int)
# 纵向外连接
data3 = pd.concat([data1, data2], join='outer', axis=0)
print(data3)
输出:
1
2
3
4
5
6
7
8
     A   B   C     D
0 0.0 4 8 NaN
1 1.0 5 9 NaN
2 2.0 6 10 NaN
3 3.0 7 11 NaN
0 NaN 12 15 18.0
1 NaN 13 16 19.0
2 NaN 14 17 20.0

1.2 主键合并数据

主键合并类似于关系型数据库,根据一个或多个键将不同的DataFrame对象连接起来,大多数是将两个DataFrame对象中重叠的列作为合并的键,Pandas中提供了用于主键合并的merge()函数,语法为:

1
pandas.merge(left,right,how='inner',on=None,left_on=None,right_on=None,left_index=False,right_index=False,sort=False,suffixes=('_x','_y'),copy=True,indicator=False,validate=None)
  • left:参与合并的左侧DataFrame对象
  • right:参与合并的右侧DataFrame对象
  • how:表示连接方式,默认为inner,该参数还支持如下取值:
    • left:使用左侧的DataFrame的键,类似SQL的左外连接
    • right:使用右侧的DataFrame的键,类似SQL的右外连接
    • outer:使用两个DataFrame所有的键,类似于SQL全连接
    • inner:使用两个DataFrame键的交集,类似于SQL内连接
  • on:用于连接的列名,必须存在于两个DataFrame对象中
  • left_on:以左侧DataFrame作为连接键
  • right_on:以右侧DataFrame作为连接键
  • left_index:左侧的行索引用作连接键
  • right_index:右侧的行索引用作连接键
  • sort:是否排序,接收布尔值,默认为False
  • suffixes:用于追加到重叠列名的末尾,默认为(_x,_y)

在使用merge()函数进行合并时默认使用重叠的列索引作为合并键,并采用内连接的方式合并数据。

1.2.1 通过主键合并

通过主键合并

针对同一个主键存在两张不同字段的表,根据主键整合到一张表里面,例如上图中左侧两张表均有索引为key的一列数据,两表通过key列合并为一个表。即:

1
2
3
4
5
6
7
8
import pandas as pd
top = pd.DataFrame({"key": ['k0', 'k3', 'k4'], "A": [1, 3, 5], "B": [2, 4, 6]})
bottom = pd.DataFrame({
"key": ['k0', 'k2', 'k3'],
"C": [7, 9, 11],
"D": [8, 10, 12]
})
print(pd.merge(top, bottom, on="key"))

输出:

1
2
3
  key  A  B   C   D
0 k0 1 2 7 8
1 k3 3 4 11 12
1.2.2 多个主键合并

多个主键合并

通过merge()函数可以对含有多个重叠列的DataFrame对象进行合并,例如上图中使用keyB作为合并键得到右侧合并结果:

1
2
3
4
5
6
7
8
9
import pandas as pd
top = pd.DataFrame({"key": ['k0', 'k3', 'k4'], "A": [1, 3, 5], "B": [2, 4, 6]})
bottom = pd.DataFrame({
"key": ['k0', 'k2', 'k3'],
"C": [7, 9, 11],
"D": [8, 10, 12],
"B": [2, 3, 1]
})
print(pd.merge(top, bottom, on=["key", "B"]))

输出为:

1
2
  key  A  B  C  D
0 k0 1 2 7 8

merge()函数支持四种合并方式分别为内连接(inner)外连接(outer)左连接(left)右连接(right)。函数中的how默认参数为inner,不同合并方式解释如下。(下列操作均针对于key为键值)

连接方式

  • 内连接(inner):匹配两个DataFrame对象中均有的键值并合并
  • 外连接(outer):合并两个DataFrame对象中所有的键值,缺失值用NaN填充
  • 左连接(left):以merge函数中第一个出现的DataFrame对象的指定键值为标准进行连接,第一个出现的DataFrame表中数据将会全部显示,而第二个出现的DataFrame对象只会显示与重叠数据行索引相同的数据,合并后缺失数据用NaN填充
  • 右连接(right):以merge函数中第二个出现的DataFrame对象的指定键值为标准进行连接,第二个出现的DataFrame表中数据将会全部显示,而第一个出现的DataFrame对象只会显示与重叠数据行索引相同的数据,合并后缺失数据用NaN填充
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
left = pd.DataFrame({
'key': ['K0', 'K1', 'K2'],
'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']
})
right = pd.DataFrame({
'key': ['K0', 'K1', 'K2', 'K3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']
})
# 左连接
data_left = pd.merge(left, right, how='left', on='key')
# 右连接
data_right = pd.merge(left, right, how='right', on='key')
# 内连接
data_inner = pd.merge(left, right, how='inner', on='key')
# 外连接
data_outer = pd.merge(left, right, how='outer', on='key')

如果两张表行索引和列索引均没有重叠部分,可以通过将merge()函数的left_indexright_index均设置为True即可,例如:

1
2
3
4
5
import pandas as pd
left = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']}, index=['a', 'b'])
right = pd.DataFrame({'C': ['C0', 'C1'], 'D': ['D0', 'D1']})
data = pd.merge(left, right, how='outer', left_index=True, right_index=True)
print(data)

输出为:

1
2
3
4
5
     A    B    C    D
a A0 B0 NaN NaN
b A1 B1 NaN NaN
0 NaN NaN C0 D0
1 NaN NaN C1 D1

1.3 根据行索引合并数据

1.3.1 join()方法

join()方法能够通过索引或者指定的列来连接DataFrame

语法:
1
join(other,on=None,how='left',lsuffix='',rsuffix='',sort=False)
  • on:用于连接的列名
  • lsuffix:接收字符串,用于在左侧重叠的列名后添加后缀
  • rsuffix:接收字符串,用于在右侧重叠的列名后添加后缀
  • sort:接收布尔值,根据连接键对合并的数据进行排序,默认为False
实例:
1
2
3
4
5
import pandas as pd
left = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']}, index=['a', 'b'])
right = pd.DataFrame({'C': ['C0', 'C1'], 'D': ['D0', 'D1']})
data = left.join(right, how="outer")
print(data)
输出:
1
2
3
4
5
     A    B    C    D
a A0 B0 NaN NaN
b A1 B1 NaN NaN
0 NaN NaN C0 D0
1 NaN NaN C1 D1

1.4 合并重叠数据

在处理数据时,当一个DataFrame对象出现了缺失值,对于这些缺失数据如果想要通过其他的DataFrame对象中的数据填充,可以通过combine_first()方法填充缺失数据。

语法:
1
combine_first(other)
  • other:用于接收填充缺失值的对象
实例:

合并重叠数据

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
from numpy import NaN
left = pd.DataFrame({
'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', NaN, 'A2', 'A3'],
'B': [NaN, 'B1', NaN, 'B3']
})
right = pd.DataFrame({
'A': ['C0', 'C1', 'C2'],
'B': ['D0', 'D1', 'D2']
},
index=[1, 2, 0])
print(left.combine_first(right))
输出:
1
2
3
4
5
    A   B key
0 A0 D2 K0
1 C0 B1 K1
2 A2 D1 K2
3 A3 B3 K3

使用combine_first方法合并两个DataFrame对象时,必须确保他们的行索引和列索引有重叠部分

2. 数据重塑

Pandas中大多数情况DataFrame对象更便于操作,因为DataFrame对象很容易获取每行或每列的数据。但是有时候需要将DataFrame对象转换为Series对象,因此需要对数据进行重塑。

2.1 重塑层次化索引

重塑层次化索引的操作主要是stack()方法和unstack()方法,前者是将数据的列旋转为行,后者是将数据的行旋转成列。

2.1.1 stack()方法
语法:
1
DataFrame.stack(level=-1,dropna=True)
  • level:表示操作的内层索引,若设置为0,表示操作外层索引,默认为-1
  • dropna:表示是否将旋转后的缺失值删除,默认为True(过滤缺失值)
实例:

重塑

1
2
3
4
import pandas as pd
data = pd.DataFrame({"A": ['A0', 'A1', 'A2'], "B": ['B0', 'B1', 'B2']})
result = data.stack()
print(result)

输出:

1
2
3
4
5
6
0  A    A0
B B0
1 A A1
B B1
2 A A2
B B2

通过:

1
print(type(result))

可查看结果为:

1
<class 'pandas.core.series.Series'>
2.1.2 unstack方法
语法:
1
DataFrame.unstack(level=-1,fill_value=None)
  • fill_value:若产生了缺失值,则可以设置这个参数用来替换NaN
实例:

将上例中还原:

1
print(result.unstack())
输出:
1
2
3
4
    A   B
0 A0 B0
1 A1 B1
2 A2 B2

可以使用stack对多层索引进行重塑。

多层索引

1
2
3
4
5
6
7
8
import pandas as pd
import numpy as np
data = pd.DataFrame(np.array([[26, 20, 22, 26], [30, 25, 24, 20]]),
index=['男生人数', '女生人数'],
columns=[['一楼', '一楼', '二楼', '二楼'],
['A教室', 'B教室', 'A教室', 'B教室']])
result = data.stack()
print(result)

输出:

1
2
3
4
5
          一楼  二楼
男生人数 A教室 26 22
B教室 20 26
女生人数 A教室 30 24
B教室 25 20

2.2 轴向旋转

2.2.1 pivot方法

pivot方法提供的功能是根据给定行索引或列索引重新组织一个DataFrame对象。

语法:
1
DataFrame.pivot(index=None,columns=None,values=None)
  • index:用于创建新DataFrame对象的行索引,如果未设置,则使用原DataFrame对象的索引。
  • columns:列索引
  • values:值
实例:
出售日期 商品名称 价格(元)
2017年5月25日 荣耀9青春版 999
2017年5月25日 小米6x 1399
2017年5月25日 OPPO A1 1399
2017年6月18日 荣耀9青春版 800
2017年6月18日 小米6x 1200
2017年6月18日 OPPO A1 1250

将上述表格转化为下表:

商品名称
出售日期
荣耀9青春版 小米6x OPPO A1
2017年5月25日 999 1399 1399
2017年6月18日 800 1200 1250
1
2
3
4
import pandas as pd
data = pd.read_excel('./ProductInfo.xlsx')
result = data.pivot(index="出售日期", columns="商品名称", values="价格(元)")
print(result)

3. 数据转换

当数据进行清洗之后,需要对数据进行一些合理的转换,使这些数据更符合分析的要求。

3.1 重命名轴索引

重命名轴索引是数据分析中比较常见的操作,可以通过rename()方法重命名个别列索引或行索引的标签或名称。

3.1.1 语法格式:
1
rename(mapper=None,index=None,columns=None,axis=None,copy=True,inplace=False,level=None)
  • index,columns:表示待转换的行索引和列索引
  • axis:表示轴的名称,可以使用indexcolumns,也可以使用数字0或1
  • copy:表示是否复制底层的数据,默认为False
  • inplace:默认为False,表示是否返回新的Pandas对象,如果设置为True,则会忽略复制的值
  • level:表示级别的名称,默认为None。对于多级索引,只重命名指定的标签。
3.1.2 实例

将下列表格列索引更改为a,b,c

A B C
0 1 2 3
1 4 5 6
2 7 8 9
3 10 11 12
1
2
3
4
5
6
7
8
import pandas as pd
data = pd.DataFrame({
"A": [1, 4, 7, 10],
"B": [2, 5, 8, 11],
"C": [3, 6, 9, 12]
})
result = data.rename(str.lower, axis="columns")
print(result)
3.1.3 输出
1
2
3
4
5
    a   b   c
0 1 2 3
1 4 5 6
2 7 8 9
3 10 11 12
3.1.4 其他说明

使用rename()方法也可以对行索引进行重命名:

1
2
result_s = result.rename(index={1: 'a', 2: 'b'})
print(result_s)

输出:

1
2
3
4
5
    a   b   c
0 1 2 3
a 4 5 6
b 7 8 9
3 10 11 12

3.2 离散化连续数据

有时候我们需要将数据进行离散化,可以使用cut()函数。

3.2.1 函数语法:
1
pandas.cut(x,bins,right=True,labels=None,retbins=False,precision=3,include_lowest=False,duplicates='raise')
  • x:表示要分箱的数组,必须是一维的
  • bins:接收int和序列类型的数据
  • right:是否包含右端点,默认包含
  • labels:用于生成区间的标签
  • retbins:是否返回bin
  • precision:精度,默认保留三位小数
  • include_lowest:是否包含左端点
3.2.2 实例
1
2
3
4
5
import pandas as pd
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 32]
bins = [0, 18, 25, 35, 60, 100]
cuts = pd.cut(ages, bins)
print(cuts)
3.2.3 输出
1
2
3
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (35, 60], (25, 35], (60, 100], (35, 60], (25, 35]]
Length: 11
Categories (5, interval[int64]): [(0, 18] < (18, 25] < (25, 35] < (35, 60] < (60, 100]]

3.3 哑变量处理类别型数据

在Pandas中,可以使用get_dummies()函数对类别特征进行哑变量处理。语法如下:

1
pandas.get_dummies(data,prefix=None,prefix_sep='_',dummy_na=False,columns=None,sparse=False,drop_first=False,dtype=None)
  • data:可接收数组,DataFrameSerise对象,表示哑变量处理的数据
  • prefix:表示列名的前缀,默认为None
  • prefix_sep:用于附加前缀作为分隔符,默认为_
  • dummy_na:表示是否为NaN值添加一列,默认为False
  • sparse:表示虚拟列是否是稀疏的,默认为False
  • drop_first:是否通过从k个分类级别中删除第一个级来获得k-1个分类级别,默认为False

例如:

1
2
3
import pandas as pd
data = pd.DataFrame({"职业": ["工人", "学生", "司机", "教师", "导游"]})
print(pd.get_dummies(data, prefix=['col_']))

输出为:

1
2
3
4
5
6
   col__司机  col__学生  col__导游  col__工人  col__教师
0 0 0 0 1 0
1 0 1 0 0 0
2 1 0 0 0 0
3 0 0 0 0 1
4 0 0 1 0 0

本文标题:Python数据预处理

文章作者:小哲

发布时间:2020年04月22日 - 09:26

最后更新:2020年05月04日 - 23:05

原始链接: 点击复制原始链接

许可协议: 协议-转载请保留原文链接及作者