合并数据集
离散化和面元划分
为了方便分析,连续数据常常被离散化或拆分为“面元”,可以看下面的例子:
ages = [20,22,25,27,21,23,37,31,61,55,29]
可以看到上面这组表示年龄的数据非常的杂乱无序,接下来需要用到cat
函数来对它们进行分割
In [90]: bins = [18, 25, 35, 60, 100]
In [91]: cats = pd.cut(ages, bins)
ca
In [92]: cats
Out[92]:
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (35, 60], (25, 35], (60, 100], (35, 60], (25, 35]]
Length: 11
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
在这里pandas返回了一个categorical对象,其结果就是cut
函数的结果所产生的面元。其中包含有了表示不同分类的类型数组。
cats
对象中的codes
属性表示数据所在分组的标签:
In [93]: cats.codes
Out[93]: array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 1], dtype=int8)
还可以对每个分组进行统计
In [94]: pd.value_counts(cats)
Out[94]:
(18, 25] 5
(25, 35] 3
(35, 60] 2
(60, 100] 1
dtype: int64
跟数学的区间符号一样,圆括号属于开端,方括号代表闭端。当然这个也是可以修改的(通过修改参数right=False
或者left=False
):
In [95]: cats = pd.cut(ages, bins,right=False)
In [96]: cats
Out[96]:
[[18, 25), [18, 25), [25, 35), [25, 35), [18, 25), ..., [35, 60), [25, 35), [60, 100), [35, 60), [25, 35)]
Length: 11
Categories (4, interval[int64]): [[18, 25) < [25, 35) < [35, 60) < [60, 100)]
这样就变成左边为闭端,右边为开端了。
如果数据太多太乱,区间表示不方便阅读也可以对每个面元区间进行命名:
In [97]: group_names = ['Youth','YoungAdult','MiddleAdult','OldAdult']
In [98]: pd.cut(ages, bins, labels = group_names)
Out[98]:
[Youth, Youth, Youth, YoungAdult, Youth, ..., MiddleAdult, YoungAdult, OldAdult, MiddleAdult, YoungAdult]
Length: 11
Categories (4, object): [Youth < YoungAdult < MiddleAdult < OldAdult]
我们分别对不同年龄段进行了命名,命名后会适用到整个categorical
对象中。
检测和过滤异常值
过滤和变换异常值在数据清理过程中是个很常用的功能,它的核心点就是数组运算:
In [103]: data = DataFrame(np.random.randn(1000,4))
In [104]: data.describe()
Out[104]:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.004025 0.014359 -0.019060 -0.011858
std 1.041391 0.984948 0.983725 0.998798
min -3.241226 -4.064789 -3.089875 -2.980788
25% -0.711723 -0.643988 -0.631027 -0.689812
50% 0.016191 0.012086 -0.060776 0.004594
75% 0.670837 0.723381 0.669204 0.644564
max 3.236404 3.262301 3.237268 2.856121
假设要从该DataFrame
中找出绝对值大于3的值:
In [105]: col = data[2] #找出第二列中绝对值大于3的值
In [106]: col[np.abs(col) > 3]
Out[106]:
74 3.237268
660 -3.089875
Name: 2, dtype: float64
如果要找出绝对值大于3的值所在的行
In [116]: data[(np.abs(data) > 3).any(1)]
Out[116]:
0 1 2 3
74 0.753715 0.476283 3.237268 -0.820739
104 -3.241226 -0.397786 -0.037499 1.565827
247 -3.063914 -0.113276 0.720286 -0.416318
394 0.067374 3.262301 0.803749 -0.366958
509 3.236404 1.009665 -0.212735 1.644595
607 -0.418046 -4.064789 -0.487579 0.754295
611 3.059083 -1.537251 1.249639 -0.300786
660 0.087979 -0.907134 -3.089875 0.235577
可以结合sign
函数对值进行约束,下面代码将data
控制在-3至3之间:
In [127]: data[np.abs(data) > 3] = np.sign(data) * 3
In [128]: data.describe()
Out[128]:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.004015 0.015162 -0.019208 -0.011858
std 1.039599 0.980267 0.982690 0.998798
min -3.000000 -3.000000 -3.000000 -2.980788
25% -0.711723 -0.643988 -0.631027 -0.689812
50% 0.016191 0.012086 -0.060776 0.004594
75% 0.670837 0.723381 0.669204 0.644564
max 3.000000 3.000000 3.000000 2.856121
排列或随机取样
利用numpy.random.permutation
函数可以对DataFrame
或者Series
进行重新排序,也可以叫随机排序。
In [135]: df = DataFrame(np.arange(5*4).reshape((5,4)))
In [136]: df
Out[136]:
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
4 16 17 18 19
In [137]: sampler = np.random.permutation(5)
In [139]: sampler
Out[139]: array([0, 4, 3, 2, 1])
In [140]: df.iloc[sampler]
Out[140]:
0 1 2 3
0 0 1 2 3
4 16 17 18 19
3 12 13 14 15
2 8 9 10 11
1 4 5 6 7
随机取样
# 在DataFrame中随机抽取了3行数据
In [144]: df.sample(n=3)
Out[144]:
0 1 2 3
4 16 17 18 19
3 12 13 14 15
1 4 5 6 7
计算指标或哑变量
是一种将分类变量(category valiable)转换成计算指标或哑变量用于统计建模或机器学习。
如果DataFrame
的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame(其值全为0或1)。pandas
中有个get_dummies
即可实现该功能。
In [149]: s = Series(list('abca'))
In [162]: s
Out[162]:
0 a
1 b
2 c
3 a
dtype: object
In [150]: pd.get_dummies(s)
Out[150]:
a b c
0 1 0 0
1 0 1 0
2 0 0 1
3 1 0 0
通过get_dummies
函数我们将Series
对象s
中的N个不同的值衍生出了N个列,衍生后的a,b,c
就是哑变量。
In [155]: df = DataFrame({'key':['b','b','a','c','a','b'],'data1':range(6)})
In [161]: df
Out[161]:
key data1
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 b 5
In [158]: pd.get_dummies(df['key'])
Out[158]:
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
同样也可以作用的DataFrame中的某一列,生成哑变量后可以做后续分析。
正则表达式
正则常用于数据的搜索与匹配,re
模块是用于正则的包,其中分为三大类:模式匹配、替换以及拆分。
拆分字符串
In [1]: import re
In [2]: text = "foo bar\t baz \t qux"
In [3]: re.split('\s+', text) #这里要拆分就必须告诉re.split要去除空白符,所以用'\s+'来表示
Out[3]: ['foo', 'bar', 'baz', 'qux']
考虑到效率方面,这样写会造成每出现一次re.split('\s+', text)
,该正则就会被编译一次,所以可以以面向对象的思路做修改:
In [4]: regex = re.compile('\s+')
In [5]: regex.split(text)
Out[5]: ['foo', 'bar', 'baz', 'qux']
这样有了regex
对象就能重复使用它而且只会编译一次正则。
如果反过来想知道这个正则在字符串中匹配到了哪些,那么可以用findall
函数
In [6]: regex.findall(text)
Out[6]: [' ', '\t ', ' \t ']
pandas的矢量化字符串函数
清理待分析的散乱数据时,经常需要将字符串规整化。
In [9]: data = {'Dave':'[email protected]','Steve':'[email protected]','Rob':'[email protected]
...: om','Wes':np.nan}
In [12]: data = pd.Series(data)
In [13]: data
Out[13]:
Dave [email protected]
Steve [email protected]
Rob [email protected]
Wes NaN
dtype: object
如果数据中有NaN
值,那在通过map
函数对数据做规整的时候会报错,有一些方法可以让我们找到哪里有空缺值:
上面例子中可以用正则来匹配到电子邮件:
In [15]: pattern = '^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$'
In [18]: data.str.findall(pattern, flags=re.IGNORECASE)
Out[18]:
Dave [.com]
Steve [.com]
Rob [.com]
Wes NaN
dtype: object
要访问列表中的元素,可以传递任意索引到这两个函数中:
In [31]: matches.str.get(2)
Out[31]:
Dave NaN
Steve NaN
Rob NaN
Wes NaN
dtype: float64
# 这两个函数是等价的
In [34]: matches.str[4]
Out[34]:
Dave NaN
Steve NaN
Rob NaN
Wes NaN
dtype: float64
常用的pandas字符串方法
总结
本章学习了数据库的合并、离散数据的划分、排列数据、哑变量、字符串规整化等。
在做数据分析之前先做清理觉得很有必要,可以确保效率并且在分析的时候避免出现问题。