[数据分析]------数据清洗与准备

1.1处理缺失值

pandas的目标之一就是尽可能无痛地处理缺失值,例如pandas对象的所有描述性统计信息默认情况下是排除缺失值的。对于数值型数据,pandas使用浮点值NaN(Not a number)来表示缺失值,在pandas中,我们将缺失值称为NA(not available)。在统计学应用中,NA数据可以是不存在的数据或者是存在但不可观察的数据(例如在数据收集过程中出现了问题)。当清洗数据用于分析时,对缺失数据本身进行分析以确定数据收集问题或数据丢失导致的数据偏差通常很重要。Python内建的None值在对象数组中也被当作NA处理。

NA处理方法:

 1.1.1过滤缺失值

有多种过滤缺失值的方法。可以使用pandas.isnull和布尔值索引手动地过滤缺失值,也可以使用dropna来过滤。在Series上使用dropna,它会返回Series中所有的非空数据及其索引值:

in:
from numpy import nan as NA
data = pd.Series([1,NA,3.5,NA,7])
data.dropna()
out:
0    1.0
2    3.5
4    7.0
dtype: float64

上面的代码等价于:

in:
data[data.notnull()]
out:
0    1.0
2    3.5
4    7.0
dtype: float64

当处理DataFrame对象时,会稍微复杂一些。dropna默认情况下会删除包含缺失值的行:

in:
data = pd.DataFrame([[1,6.5,3],[1,NA,NA],[NA,NA,NA],[NA,6.5,3]])
data
out:
0	1	2
0	1.0	6.5	3.0
1	1.0	NaN	NaN
2	NaN	NaN	NaN
3	NaN	6.5	3.0

in:
data.dropna()
out:
	0	1	2
0	1.0	6.5	3.0

传入how = ‘all’,将删除所有值均为NA的行:

in:
data.dropna(how = 'all')
out:
0	1	2
0	1.0	6.5	3.0
1	1.0	NaN	NaN
3	NaN	6.5	3.0

如果要对列进行同样的操作,只需传入参数axis = 1

in:
data.dropna(how = 'all',axis = 1)
out:
	0	1	2
0	1.0	6.5	3.0
1	1.0	NaN	NaN
2	NaN	NaN	NaN
3	NaN	6.5	3.0

若想保留一定数量的观察值的行或列,可以用thresh参数来表示:

in:
df = pd.DataFrame(np.random.randn(7,3))
df.iloc[:4,1] = NA
df.iloc[:2,2] = NA
df
out:
           0	1	2
0	-0.087487	NaN	NaN
1	-0.763685	NaN	NaN
2	0.768197	NaN	0.989586
3	-0.793039	NaN	0.515711
4	0.813376	1.179414	-1.997658
5	0.114463	1.775586	-0.096684
6	-0.538589	0.783257	-2.108953

in:
df.dropna(thresh = 2)
out:

           0	1	2
2	0.768197	NaN	0.989586
3	-0.793039	NaN	0.515711
4	0.813376	1.179414	-1.997658
5	0.114463	1.775586	-0.096684
6	-0.538589	0.783257	-2.108953

1.1.2补全缺失值

大多数情况下,主要使用fillna方法来补全缺失值。调用fillna时,可以使用一个常数来代替缺失值:

in:
df.fillna(0)
out:
	        0	      1	        2
0	-0.087487	0.000000	0.000000
1	-0.763685	0.000000	0.000000
2	0.768197	0.000000	0.989586
3	-0.793039	0.000000	0.515711
4	0.813376	1.179414	-1.997658
5	0.114463	1.775586	-0.096684
6	-0.538589	0.783257	-2.108953

在调用fillna时使用字典,可以为不同列设定不同的填充值

in:
df.fillna({1:0.5,2:0})
out:
	       0        1	       2
0	-0.087487	0.500000	0.000000
1	-0.763685	0.500000	0.000000
2	0.768197	0.500000	0.989586
3	-0.793039	0.500000	0.515711
4	0.813376	1.179414	-1.997658
5	0.114463	1.775586	-0.096684
6	-0.538589	0.783257	-2.108953

fillna返回的是一个新的对象,但也可以修改已经存在的对象:

in:
_ = df.fillna(0,inplace = True)
#或df.fillna(0,inplace = True)
df
out:

           0	       1	       2
0	-0.855959	0.000000	0.000000
1	-2.634261	0.000000	0.000000
2	-1.618698	0.000000	0.745549
3	-0.631199	0.000000	-0.521110
4	0.316833	-0.661527	-0.295758
5	-0.348348	-1.188701	0.856542
6	1.151774	0.164366	-0.645428
in: 
df.fillna(method = 'bfill')  #bfill 是向前填充  ffill是向后填充
out:
            0	       1	       2
0	-0.900219	0.582162	2.132188
1	-0.837954	0.582162	2.132188
2	0.425441	0.582162	2.132188
3	1.232482	0.582162	0.293976
4	-0.926626	0.582162	-0.681543
5	0.589723	-0.721490	0.665564
6	-0.808692	0.419491	-0.116198

in:
df.fillna(method = 'bfill',limit = 2) #用limit限制每列或每行可以替代NaN的数目
out:

            0	  1	   2
0	-0.900219	NaN	2.132188
1	-0.837954	NaN	2.132188
2	0.425441	0.582162	2.132188
3	1.232482	0.582162	0.293976
4	-0.926626	0.582162	-0.681543
5	0.589723	-0.721490	0.665564
6	-0.808692	0.419491	-0.116198

可以将平均数或中位数用于填充缺失值,如:

data.fillna(data.mean())

fillna函数参数:

 1.2数据转换

1.2.1删除重复值

in:
data = pd.DataFrame({'k1':['one','two']*3+['two'],'k2':[1,1,2,3,3,4,4]})
data
out:

    k1  k2
0	one	1
1	two	1
2	one	2
3	two	3
4	one	3
5	two	4
6	two	4

DataFrame的duplicated方法返回的是一个布尔值Series,这个Series反映的是每一行是否存在重复:

in:
data.duplicated()
out:
0    False
1    False
2    False
3    False
4    False
5    False
6     True
dtype: bool

drop_duplicates返回的是DataFrame,内容是duplicated返回数组中为False的部分:

in:
data.drop_duplicates()
out:
    k1	k2
0	one	1
1	two	1
2	one	2
3	two	3
4	one	3
5	two	4

这些方法都是默认对列进行操作,可以指定数据的任何子集来检测是否有重复。

in:
data['v1'] = range(7)
data
out:

    k1	k2	v1
0	one	1	0
1	two	1	1
2	one	2	2
3	two	3	3
4	one	3	4
5	two	4	5
6	two	4	6
in:
data.drop_duplicates(['k1'])
out:
    k1	k2	v1
0	one	1	0
1	two	1	1

duplicated和drop_duplicates默认都是保留第一个观测到的值,传入参数keep = 'last',将会返回最后一个

1.2.2使用函数或映射进行数据转换

 对于许多数据集,可能希望基于DataFrame中的数组、列或列中的数值进行一些转换。

对于如下数据:

in:
data = pd.DataFrame({'food':['bacon','pulled pork','bacon','pastrami','corned     
      beef','bacon','pastrami','honey ham','nova lox'],'ounces':[4,3,12,6,7,8,3,5,6]})
data
out:

   food	ounces
0	bacon	4
1	pulled pork	3
2	bacon	12
3	pastrami	6
4	corned beef	7
5	bacon	8
6	pastrami	3
7	honey ham	5
8	nova lox	6

要添加一列用于表明每种食物的动物肉类型。先写一个事物和肉类的映射:

in:
meat_to_animal = {
    'bacon':'pig',
    'pulled pork':'pig',
    'pastrami':'cow',
    'corned beef':'cow',
    'honey ham':'pig',
    'nova lox':'salmon'
}

Series的map方法接受一个函数或一个包含映射关系的字典型对象。

in:
data['animal'] = data['food'].map(meat_to_animal)
data
out:
	food	ounces	animal
0	bacon	4	pig
1	pulled pork	3	pig
2	bacon	12	pig
3	pastrami	6	cow
4	corned beef	7	cow
5	bacon	8	pig
6	pastrami	3	cow
7	honey ham	5	pig
8	nova lox	6	salmon

使用map是一种可以便携执行按元素转换及其他清洗相关操作的方法

1.2.3替代值

可以用replace来进行替换某些值,replace方法一般是生成新的数据,若要修改原数据,需要传入inplace = True:

in:
data = pd.Series([1,-999,2,-999,-100,3])
data
out:
0      1
1   -999
2      2
3   -999
4   -100
5      3
dtype: int64

in:
data.replace(-999,np.nan)
out:
0      1.0
1      NaN
2      2.0
3      NaN
4   -100.0
5      3.0
dtype: float64

如果想一次替代多个值,可以传入一个列表和一个替代值:

in:
data.replace([-999,-100],np.nan)
out:
0    1.0
1    NaN
2    2.0
3    NaN
4    NaN
5    3.0
dtype: float64

要将不同的值替换为不同的值,可以传入替代值的列表:

in:
data.replace([-999,-100],[np.nan,0])
out:
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

参数也可以通过字典进行传递:

in:
data.replace({-999:np.nan,-100:0})
out:
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

1.2.4重命名轴索引

与Series类似,轴索引也有一个map方法,结合该方法可以更改轴索引

in:
data = pd.DataFrame(np.arange(12).reshape((3,4)),index = ['ohio','colorado','new york'],columns = ['one','two','three','four'])
data
out:

         one two three four
ohio	    0	1	2	3
colorado	4	5	6	7
new york	8	9	10	11
in:
transform = lambda x: x[:4].upper()
data.index.map(transform)
out:
Index(['OHIO', 'COLO', 'NEW '], dtype='object')
in:
data.index = data.index.map(transform)
data
out:

one	two	three	four
OHIO	0	1	2	3
COLO	4	5	6	7
NEW	8	9	10	11

如果想要创建数据集转换后的版本,并且不修改原有的数据集,一个有用的方法是rename:

in:
data.rename(index = str.title,columns = str.upper)
out:
	ONE	TWO	THREE	FOUR
Ohio	0	1	2	3
Colo	4	5	6	7
New	8	9	10	11

rename可以结合字典型对象使用,为轴标签的子集提供新的值:

in:
data.rename(index = {'OHIO':'indina'})
out:
	one	two	three	four
indina	0	1	2	3
COLO	4	5	6	7
NEW	8	9	10	11

1.2.5离散化和分箱

连续值经常需要进行离散化,或者分离成“箱子”进行研究。比如年龄:

为了实现年龄的分组,可以使用pandas中的cut

in:
ages = [20,22,25,27,21,23,37,31,64,45,41,32]
bins = [18,25,35,60,100]
cats = pd.cut(ages,bins)
cats
out:
[(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, right]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]

 pandas返回的对象是一个特殊的Categorical对象。我们看到的输出描述了由pandas.cut计算出的箱。可以把它当作一个表示箱名的字符串数组,在内部包含一个categoreis(类别)数组,它制定了不同的类别名称以及codes属性中的ages(年龄)数据标签:

in:
cats.codes
out:
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
in:
cats.categories
out:
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]], dtype='interval[int64, right]')
in:
pd.value_counts(cats)
out:
(18, 25]     5
(25, 35]     3
(35, 60]     3
(60, 100]    1
dtype: int64

pd.value_counts(cats)是对pandas.cut的结果中的箱数量的计数

与区间的数学符号一致,小括号表示边是开放的,中括号表示它是封闭的,即闭区间。可以通过传递right = False来改变哪一边是封闭的。

也可以通过向labels选项传递一个列表或数组来传入自定义的箱名

in:
group_names = ['youths','youngadult','middleaged','senior']
pd.cut(ages,bins,labels = group_names)
out:
['youths', 'youths', 'youths', 'youngadult', 'youths', ..., 'youngadult', 'senior', 'middleaged', 'middleaged', 'youngadult']
Length: 12
Categories (4, object): ['youths' < 'youngadult' < 'middleaged' < 'senior']

如果传入cut的不是显示的箱边,而是箱的数量(整数),pandas将根据数据中的最小值和最大值来计算出等长的箱

in:
data = np.random.rand(20)
pd.cut(data,4,precision = 2)
out:
[(0.06, 0.26], (0.26, 0.47], (0.47, 0.67], (0.47, 0.67], (0.47, 0.67], ..., (0.67, 0.88], (0.06, 0.26], (0.26, 0.47], (0.67, 0.88], (0.47, 0.67]]
Length: 20
Categories (4, interval[float64, right]): [(0.06, 0.26] < (0.26, 0.47] < (0.47, 0.67] < (0.67, 0.88]]

precision = 2 的选项将十进制精度限制在两位

qcut是一个与分箱密切相关的函数,基于样本分位数进行分箱。取决于数据的分布,使用cut通常不会使每个箱具有相同数据量的数据点。由于qcut使用样本的分位数,可以通过qcut获得等长的箱。

in:
data = np.random.randn(1000)  #正态分布
cats = pd.qcut(data,4)  #切成4份
cats
out:
[(-3.645, -0.703], (-0.0383, 0.668], (-0.703, -0.0383], (-3.645, -0.703], (-0.0383, 0.668], ..., (-0.703, -0.0383], (-0.0383, 0.668], (-0.0383, 0.668], (0.668, 3.156], (-3.645, -0.703]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.645, -0.703] < (-0.703, -0.0383] < (-0.0383, 0.668] < (0.668, 3.156]]

in:
pd.value_counts(cats)
out:
(-3.645, -0.703]     250
(-0.703, -0.0383]    250
(-0.0383, 0.668]     250
(0.668, 3.156]       250
dtype: int64

与cut类似,可以传入自定义的分位数(0和1之间的数据,包括边)

in:
pd.qcut(data,[0,0.1,0.5,0.9,1])
out:
[(-1.32, -0.0383], (-0.0383, 1.289], (-1.32, -0.0383], (-3.645, -1.32], (-0.0383, 1.289], ..., (-1.32, -0.0383], (-0.0383, 1.289], (-0.0383, 1.289], (1.289, 3.156], (-3.645, -1.32]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.645, -1.32] < (-1.32, -0.0383] < (-0.0383, 1.289] < (1.289, 3.156]]

1.2.6检测和过滤异常值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值