目录

1. 处理缺失值

1.1 过滤缺失值

1.2 补全缺失值

2. 数据转换

2.1 删除重复值

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

2.3 替代值

2.4 重命名轴索引

2.5 离散化和分箱

2.6 检测和过滤异常值

2.7 置换和随机抽样

2.8 计算指标/虚拟变量

3 字符串操作

3.1 字符串对象方法

3.2 正则表达式

3.3 pandas中的向量化字符串函数


1. 处理缺失值

pandas对象的所有描述性统计信息默认情况下是排除缺失值的。

对于数值型数据,pandas使用浮点值NaN(Not a Number来表示缺失值)。称NaN为容易检测到的标识值:

>>> string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
>>> string_data
0     aardvark
1    artichoke
2          NaN
3      avocado
dtype: object>>> string_data.isnull()
0    False
1    False
2     True
3    False
dtype: bool

在pandas中,我们采用了R语言中的编程惯例,将缺失值成为NA,意思是not available(不可用)。在统计学应用中,NA数据可以是不存在的数据或者是存在但不可观察的数据(例如在数据收集过程中出现了问题)。当清洗数据用于分析时,对缺失数据本身进行分析以确定数据收集问题或数据丢失导致的数据偏差通常很重要。

Python内建的None值在对象数组中也被当作NA处理:

>>> string_data[0] = None
>>> string_data.isnull()
0     True
1    False
2     True
3    False
dtype: bool

NA处理方法

1.1 过滤缺失值

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

>>> from numpy import nan as NA
>>> data = pd.Series([1, NA, 3.5, NA, 7])
>>> data.dropna()
0    1.0
2    3.5
4    7.0
dtype: float64# 另一种写法
>>> data[data.notnull()]
0    1.0
2    3.5
4    7.0
dtype: float64

当处理DataFrame对象时,事情会稍微更复杂一点。你可能想要删除全部为NA或包含有NA的行或列。dropna默认情况下会删除包含缺失值的行:

>>> data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
>>>                      [NA, NA, NA], [NA, 6.5, 3.]])>>> cleaned = data.dropna()
>>> data0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0>>> cleaned0    1    2
0  1.0  6.5  3.0

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

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

如果要用同样的方式去删除列,传入参数axis=1:

>>> data[4] = NA
>>> data0    1    2   4
0  1.0  6.5  3.0 NaN
1  1.0  NaN  NaN NaN
2  NaN  NaN  NaN NaN
3  NaN  6.5  3.0 NaN>>> data.dropna(axis=1, how='all')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

过滤DataFrame的行的相关方法往往涉及时间序列数据。假设只想保留包含一定数量的观察值的行。可以用thresh参数来表示:

>>> df = pd.DataFrame(np.random.randn(7, 3))
>>> df.iloc[:4, 1] = NA
>>> df.iloc[:2, 2] = NA
>>> df0         1         2
0 -0.204708       NaN       NaN
1 -0.555730       NaN       NaN
2  0.092908       NaN  0.769023
3  1.246435       NaN -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741>>> df.dropna()0         1         2
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741>>> df.dropna(thresh=2)0         1         2
2  0.092908       NaN  0.769023
3  1.246435       NaN -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

1.2 补全缺失值

有时可能需要以多种方式补全“漏洞”,而不是过滤缺失值(也可能丢弃其他数据)。大多数情况下,主要使用fillna方法来补全缺失值。调用fillna时,可以使用一个常数来替代缺失值:

>>> df.fillna(0)0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

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

>>> df.fillna({1: 0.5, 2: 0})0         1         2
0 -0.204708  0.500000  0.000000
1 -0.555730  0.500000  0.000000
2  0.092908  0.500000  0.769023
3  1.246435  0.500000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

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

>>> _ = df.fillna(0, inplace=True)
>>> df0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

用于重建索引的相同的插值方法也可以用于fillna:

>>> df = pd.DataFrame(np.random.randn(6, 3))
>>> df.iloc[2:, 1] = NA
>>> df.iloc[4:, 2] = NA
>>> df0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772       NaN  1.343810
3 -0.713544       NaN -2.370232
4 -1.860761       NaN       NaN
5 -1.265934       NaN       NaN>>> df.fillna(method='ffill')0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761  0.124121 -2.370232
5 -1.265934  0.124121 -2.370232>>> df.fillna(method='ffill', limit=2)0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761       NaN -2.370232
5 -1.265934       NaN -2.370232

使用fillna可以完成很多带有一点创造性的工作。例如,可以将Series的平均值或中位数用于填充缺失值:

>>> data = pd.Series([1., NA, 3.5, NA, 7])
>>> data.fillna(data.mean())
0    1.000000
1    3.833333
2    3.500000
3    3.833333
4    7.000000
dtype: float64

fillna函数参数

2. 数据转换

2.1 删除重复值

由于各种原因,DataFrame中会出现重复行。

>>> data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
>>>                      'k2': [1, 1, 2, 3, 3, 4, 4]})>>> datak1  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反映的是每一行是否存在重复(与之前出现过的行相同)情况:

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

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

>>> data.drop_duplicates()k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4

这些方法默认都是对列进行操作。可以指定数据的任何子集来检测是否有重复。假设我们有一个额外的列,并想基于'k1'列去除重复值:

>>> data['v1'] = range(7)
>>> data.drop_duplicates(['k1'])k1  k2  v1
0  one   1   0
1  two   1   1

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

>>> data.drop_duplicates(['k1', 'k2'], keep='last')k1  k2  v1
0  one   1   0
1  two   1   1
2  one   2   2
3  two   3   3
4  one   3   4
6  two   4   6

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

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

# 关于肉类的假设数据
>>> data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon',
>>>                            'Pastrami', 'corned beef', 'Bacon',
>>>                           'pastrami', 'honey ham', 'nova lox'],
>>>                   'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})>>> datafood  ounces
0        bacon     4.0
1  pulled pork     3.0
2        bacon    12.0
3     Pastrami     6.0
4  corned beef     7.5
5        Bacon     8.0
6     pastrami     3.0
7    honey ham     5.0
8     nova lox     6.0

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

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

Series的map方法接收一个函数或一个包含映射关系的字典型对象,但是这里我们有一个小的问题在于一些肉类大写了,而另一部分肉类没有。因此,需要使用Series的str.lower方法将每个值都转换为小写:

>>> lowercased = data['food'].str.lower()
>>> lowercased
0          bacon
1    pulled pork
2          bacon
3       pastrami
4    corned beef
5          bacon
6       pastrami
7      honey ham
8       nova lox
Name: food, dtype: object>>> data['animal'] = lowercased.map(meat_to_animal)
>>> datafood  ounces  animal
0        bacon     4.0     pig
1  pulled pork     3.0     pig
2        bacon    12.0     pig
3     Pastrami     6.0     cow
4  corned beef     7.5     cow
5        Bacon     8.0     pig
6     pastrami     3.0     cow
7    honey ham     5.0     pig
8     nova lox     6.0  salmon

也可以传入一个能够完成所有工作的函数:

>>> data['food'].map(lambda x: meat_to_animal[x.lower()])
0       pig
1       pig
2       pig
3       cow
4       cow
5       pig
6       cow
7       pig
8    salmon
Name: food, dtype: object

2.3 替代值

使用fillna填充缺失值是通用值替换的特殊案例。map可以用来修改一个对象中的子集的值,但是replace提供了更为简单灵活的实现。

>>> data = pd.Series([1., -999., 2., -999., -1000., 3.])
>>> data
0       1.0
1    -999.0
2       2.0
3    -999.0
4   -1000.0
5       3.0
dtype: float64

-999可能是缺失值的标识。如果要使用NA来替代这些值,可以使用replace方法生成新的Series(除非传入了inplace=True):

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

一次替代多个值,可以传入一个列表和替代值

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

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

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

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

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

data.replace方法与data.str.replace方法是不同的,data.str.replace是对字符串进行按元素替代的。

2.4 重命名轴索引

和Series中的值一样,可以通过函数或某种形式的映射对轴标签进行类似的转换,生成新的且带有不同标签的对象。也可以在不生成新的数据结构的情况下修改轴。

>>> data = pd.DataFrame(np.arange(12).reshape((3, 4)),
>>>                     index=['Ohio', 'Colorado', 'New York'],
>>>                     columns=['one', 'two', 'three', 'four'])

与Series类似,轴索引也有一个map方法:

>>> transform = lambda x: x[:4].upper()
>>> data.index.map(transform)
Index(['OHIO', 'COLO', 'NEW '], dtype='object')

赋值给index,修改DataFrame:

>>> data.index = data.index.map(transform)
>>> dataone  two  three  four
OHIO    0    1      2     3
COLO    4    5      6     7
NEW     8    9     10    11

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

>>> data.rename(index={'OHIO': 'INDIANA'},
>>>             columns={'three': 'peekaboo'}) one  two  peekaboo  four
INDIANA    0    1         2     3
COLO       4    5         6     7
NEW        8    9        10    11

rename可以让你从手动复制DataFrame并为其分配索引和列属性的烦琐工作中解放出来。如果想要修改原有的数据集,传入inplace=True:

>>> data.rename(index={'OHIO': 'INDIANA'}, inplace=True)
>>> dataone  two  three  four
INDIANA    0    1      2     3
COLO       4    5      6     7
NEW        8    9     10    11

2.5 离散化和分箱

连续值经常需要离散化,或者分离成”箱子“进行分析。假设有某项研究中一组人群的数据,将他们进行分组,放入离散的年龄框中:

>>> ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

将这些年龄分为18~25、26~35、36~60以及61及以上等若干组。使用pandas中的cut:

>>> bins = [18, 25, 35, 60, 100]>>> cats = pd.cut(ages, bins)
>>> cats
[(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]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]

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

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

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

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

>>> pd.cut(ages, [18, 26, 36, 61, 100], right=False)
[[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36,61), [36, 61), [26, 36)]
Length: 12
Categories (4, interval[int64]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]

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

>>> group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
>>> pd.cut(ages, bins, labels=group_names)
[Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, Mid
dleAged, YoungAdult]
Length: 12
Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]

如果你传给cut整数个的箱来代替显式的箱边,pandas将根据数据中的最小值和最大值计算出等长的箱。请考虑一些均匀分布的数据被切成四份的情况:

>>> data = np.random.rand(20)
>>> pd.cut(data, 4, precision=2)
[(0.34, 0.55], (0.34, 0.55], (0.76, 0.97], (0.76, 0.97], (0.34, 0.55], ..., (0.34
, 0.55], (0.34, 0.55], (0.55, 0.76], (0.34, 0.55], (0.12, 0.34]]
Length: 20
Categories (4, interval[float64]): [(0.12, 0.34] < (0.34, 0.55] < (0.55, 0.76] <
(0.76, 0.97]]

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

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

>>> data = np.random.randn(1000)  # 正态分布
>>> cats = pd.qcut(data, 4)  # 切成四份
>>> cats
[(-0.0265, 0.62], (0.62, 3.928], (-0.68, -0.0265], (0.62, 3.928], (-0.0265, 0.62]
, ..., (-0.68, -0.0265], (-0.68, -0.0265], (-2.95, -0.68], (0.62, 3.928], (-0.68,-0.0265]]
Length: 1000
Categories (4, interval[float64]): [(-2.95, -0.68] < (-0.68, -0.0265] < (-0.0265,0.62] <(0.62, 3.928]]>>> pd.value_counts(cats)
(0.62, 3.928]       250
(-0.0265, 0.62]     250
(-0.68, -0.0265]    250
(-2.95, -0.68]      250
dtype: int64

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

>>> pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.])
[(-0.0265, 1.286], (-0.0265, 1.286], (-1.187, -0.0265], (-0.0265, 1.286], (-0.026
5, 1.286], ..., (-1.187, -0.0265], (-1.187, -0.0265], (-2.95, -1.187], (-0.0265,
1.286], (-1.187, -0.0265]]
Length: 1000
Categories (4, interval[float64]): [(-2.95, -1.187] < (-1.187, -0.0265] < (-0.026
5, 1.286] <(1.286, 3.928]]

2.6 检测和过滤异常值

过滤或转换异常值在很大程度上是应用数组操作的事情。考虑一个具有正态分布数据的DataFrame:

>>> data = pd.DataFrame(np.random.randn(1000, 4))
>>> data.describe()0            1            2            3
count  1000.000000  1000.000000  1000.000000  1000.000000
mean      0.049091     0.026112    -0.002544    -0.051827
std       0.996947     1.007458     0.995232     0.998311
min      -3.645860    -3.184377    -3.745356    -3.428254
25%      -0.599807    -0.612162    -0.687373    -0.747478
50%       0.047101    -0.013609    -0.022158    -0.088274
75%       0.756646     0.695298     0.699046     0.623331
max       2.653656     3.525865     2.735527     3.366626

想要找出一列中绝对值大于三的值:

>>> col = data[2]
>>> col[np.abs(col) > 3]
41    -3.399312
136   -3.745356
Name: 2, dtype: float64

要选出所有值大于3或小于-3的行,你可以对布尔值DataFrame使用any方法:

>>> data[(np.abs(data) > 3).any(1)]0         1         2         3
41   0.457246 -0.025907 -3.399312 -0.974657
60   1.951312  3.260383  0.963301  1.201206
136  0.508391 -0.196713 -3.745356 -1.520113
235 -0.242459 -3.056990  1.918403 -0.578828
258  0.682841  0.326045  0.425384 -3.428254
322  1.179227 -3.184377  1.369891 -1.074833
544 -3.548824  1.553205 -2.186301  1.277104
635 -0.578093  0.193299  1.397822  3.366626
782 -0.207434  3.525865  0.283070  0.544635
803 -3.645860  0.255475 -0.549574 -1.907459

值可以根据这些标准来设置,下面代码限制了-3到3之间的数值:

>>> data[np.abs(data) > 3] = np.sign(data) * 3
>>> data.describe()0            1            2            3
count  1000.000000  1000.000000  1000.000000  1000.000000
mean      0.050286     0.025567    -0.001399    -0.051765
std       0.992920     1.004214     0.991414     0.995761
min      -3.000000    -3.000000    -3.000000    -3.000000
25%      -0.599807    -0.612162    -0.687373    -0.747478
50%       0.047101    -0.013609    -0.022158    -0.088274
75%       0.756646     0.695298     0.699046     0.623331
max       2.653656     3.000000     2.735527     3.000000

语句np.sign(data)根据数据中的值的正负分别生成1和-1的数值:

>>> np.sign(data).head()0    1    2    3
0 -1.0  1.0 -1.0  1.0
1  1.0 -1.0  1.0 -1.0
2  1.0  1.0  1.0 -1.0
3 -1.0 -1.0  1.0 -1.0
4 -1.0  1.0 -1.0 -1.0

2.7 置换和随机抽样

使用numpy.random.permutation对DataFrame中的Series或行进行置换(随机重排序)是非常方便的。在调用permutation时根据你想要的轴长度可以产生一个表示新顺序的整数数组:

>>> df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4)))
>>> sampler = np.random.permutation(5)
>>> sampler
array([3, 1, 4, 2, 0])

整数数组可以用在基于iloc的索引或等价的take函数中:

>>> df0   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>>> df.take(sampler)0   1   2   3
3  12  13  14  15
1   4   5   6   7
4  16  17  18  19
2   8   9  10  11
0   0   1   2   3

要选出一个不含有替代值的随机子集,你可以使用Series和DataFrame的sample方法:

>>> df.sample(n=3)0   1   2   3
3  12  13  14  15
4  16  17  18  19
2   8   9  10  11

要生成一个带有替代值的样本(允许有重复选择),将replace=True传入sample方法:

>>> choices = pd.Series([5, 7, -1, 6, 4])
>>> draws = choices.sample(n=10, replace=True)
>>> draws
4    4
1    7
4    4
2   -1
0    5
3    6
1    7
4    4
0    5
4    4
dtype: int64

2.8 计算指标/虚拟变量

将分类变量转换为“虚拟”或“指标”矩阵是另一种用于统计建模或机器学习的转换操作。如果DataFrame中的一列有k个不同的值,则可以衍生一个k列的值为1和0的矩阵或DataFrame。pandas有一个get_dummies函数用于实现该功能。

>>> df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
>>>                    'data1': range(6)})
>>> pd.get_dummies(df['key'])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的列上加入前缀,然后与其他数据合并。在get_dummies方法中有一个前缀参数用于实现该功能:

>>> dummies = pd.get_dummies(df['key'], prefix='key')
>>> df_with_dummy = df[['data1']].join(dummies)
>>> df_with_dummy data1  key_a  key_b  key_c
0      0      0      1      0
1      1      0      1      0
2      2      1      0      0
3      3      0      0      1
4      4      1      0      0
5      5      0      1      0

如果DataFrame中的一行属于多个类别,则情况略为复杂。

>>> mnames = ['movie_id', 'title', 'genres']
>>> movies = pd.read_table('datasets/movielens/movies.dat', sep='::',
>>>                     header=None, names=mnames)
>>> movies[:10]movie_id                                title                        genres
0        1                 Toy Story (1995)    Animation|Children's|Comedy
1        2                   Jumanji (1995)    Adventure|Children's|Fantasy
2        3           Grumpier Old Men (1995)               Comedy|Romance
3        4          Waiting to Exhale (1995)                 Comedy|Drama
4        5  Father of the Bride Part II (1995)                        Comedy
5        6                      Heat (1995)        Action|Crime|Thriller
6        7                   Sabrina (1995)               Comedy|Romance
7        8              Tom and Huck (1995)         Adventure|Children's
8        9               Sudden Death (1995)                       Action
9       10                 GoldenEye (1995)    Action|Adventure|Thriller

为每个电影流派添加指标变量需要进行一些数据处理。首先,从数据集中提取出所有不同的流派的列表:

>>> all_genres = []
>>> for x in movies.genres:
>>>     all_genres.extend(x.split('|'))
>>> genres = pd.unique(all_genres)
>>> genres
array(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy','Romance', 'Drama', 'Action', 'Crime', 'Thriller', 'Horror','Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir','Western'], dtype=object)

使用全0的DataFrame是构建指标DataFrame的一种方式:

>>> zero_matrix = np.zeros((len(movies), len(genres)))
>>> dummies = pd.DataFrame(zero_matrix, columns=genres)

现在,遍历每一部电影,将dummies每一行的条目设置为1。为了实现该功能,使用dummies.columns来计算每一个流派的列指标:

>>> gen = movies.genres[0]
>>> gen.split('|')
['Animation', "Children's", 'Comedy']>>> dummies.columns.get_indexer(gen.split('|'))
array([0, 1, 2])

之后,使用.loc根据这些指标来设置值:

>>> for i, gen in enumerate(movies.genres):
>>>     indices = dummies.columns.get_indexer(gen.split('|'))
>>>     dummies.iloc[i, indices] = 1

之后,和前面一样,可以将结果与movies进行联合:

>>> movies_windic = movies.join(dummies.add_prefix('Genre_'))
>>> movies_windic.iloc[0]
movie_id                                       1
title                           Toy Story (1995)
genres               Animation|Children's|Comedy
Genre_Animation                                1
Genre_Children's                               1
Genre_Comedy                                   1
Genre_Adventure                                0
Genre_Fantasy                                  0
Genre_Romance                                  0
Genre_Drama                                    0...
Genre_Crime                                    0
Genre_Thriller                                 0
Genre_Horror                                   0
Genre_Sci-Fi                                   0
Genre_Documentary                              0
Genre_War                                      0
Genre_Musical                                  0
Genre_Mystery                                  0
Genre_Film-Noir                                0
Genre_Western                                  0
Name: 0, Length: 21, dtype: object

对于更大的数据,上面这种使用多成员构建指标变量并不是特别快速。更好的方法是写一个直接将数据写为NumPy数组的底层函数,然后将结果封装进DataFrame。

将get_dummies与cut等离散化函数结合使用是统计应用的一个有用方法:

>>> np.random.seed(12345)
>>> values = np.random.rand(10)
>>> values
array([ 0.9296,  0.3164,  0.1839,  0.2046,  0.5677,  0.5955,  0.9645,0.6532,  0.7489,  0.6536])>>> bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
>>> pd.get_dummies(pd.cut(values, bins))(0.0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1.0]
0           0           0           0           0           1
1           0           1           0           0           0
2           1           0           0           0           0
3           0           1           0           0           0
4           0           0           1           0           0
5           0           0           1           0           0
6           0           0           0           0           1
7           0           0           0           1           0
8           0           0           0           1           0
9           0           0           0           1           0

使用numpy.random.seed来设置随机种子以确保示例的确定性。

3 字符串操作

3.1 字符串对象方法

在很多字符串处理和脚本应用中,内建的字符串方法是足够的。例如,一个逗号分隔的字符串可以使用split方法拆分成多块:

>>> val = 'a,b,  guido'
>>> val.split(',')
['a', 'b', '  guido']

split常和strip一起使用,用于清除空格(包括换行):

>>> pieces = [x.strip() for x in val.split(',')]
>>> pieces
['a', 'b', 'guido']

这些子字符串可以使用加法与两个冒号分隔符连接在一起:

>>> first, second, third = pieces
>>> first + '::' + second + '::' + third
'a::b::guido'

但是这并不是一个实用的通用方法。在字符串'::'的join方法中传入一个列表或元组是一种更快且更加Pythonic(Python风格化)的方法:

>>> '::'.join(pieces)
'a::b::guido'

其他方法涉及定位子字符串。使用Python的in关键字是检测子字符串的最佳方法,尽管index和find也能实现同样的功能:

>>> 'guido' in val
True>>> val.index(',')
1>>> val.find(':')
-1

find和index的区别在于index在字符串没有找到时会抛出一个异常(而find是返回-1)。

相关地,count返回的是某个特定的子字符串在字符串中出现的次数:

>>> val.count(',')
2

replace将用一种模式替代另一种模式。它通常也用于传入空字符串来删除某个模式。

>>> val.replace(',', '::')
'a::b::  guido'>>> val.replace(',', '')
'ab  guido'

Python内建字符串方法

3.2 正则表达式

正则表达式提供了一种在文本中灵活查找或匹配(通常更为复杂的)字符串模式的方法。单个表达式通常被称为regex,是根据正则表达式语言形成的字符串。Python内建的re模块是用于将正则表达式应用到字符串上的库。

re模块主要有三个主题:模式匹配、替代、拆分。当然,这三部分主题是相关联的。一个正则表达式描述了在文本中需要定位的一种模式,可以用于多种目标。让我们来看一个简单的示例:假设我们想将含有多种空白字符(制表符、空格、换行符)的字符串拆分开。描述一个或多个空白字符的正则表达式是\s+:

>>> import re
>>> text = "foo    bar\t baz  \tqux"
>>> re.split('\s+', text)
['foo', 'bar', 'baz', 'qux']

当你调用re.split('\s+',text),正则表达式首先会被编译,然后正则表达式的split方法在传入文本上被调用。你可以使用re.compile自行编译,形成一个可复用的正则表达式对象:

>>> regex = re.compile('\s+')
>>> regex.split(text)
['foo', 'bar', 'baz', 'qux']

如果你想获得的是一个所有匹配正则表达式的模式的列表,你可以使用findall方法:

>>> regex.findall(text)
['    ', '\t ', '  \t']

为了在正则表达式中避免转义符\的影响,可以使用原生字符串语法,比如r'C:\x'或者用等价的'C:\\x'

将相同的表达式应用到多个字符串上,使用re.compile创建一个正则表达式对象,这样做有利于节约CPU周期。

match和search与findall相关性很大。findall返回的是字符串中所有的匹配项,而search返回的仅仅是第一个匹配项。match更为严格,它只在字符串的起始位置进行匹配。

text = """Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com
"""
pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'
# re.IGNORECASE使正则表达式不区分大小写
regex = re.compile(pattern, flags=re.IGNORECASE)

在文本上使用findall会生成一个电子邮件地址的列表:

>>> regex.findall(text)
['dave@google.com','steve@gmail.com','rob@gmail.com','ryan@yahoo.com']

search返回的是文本中第一个匹配到的电子邮件地址。对于前面提到的正则表达式,匹配对象只能告诉我们模式在字符串中起始和结束的位置:

>>> m = regex.search(text)
>>> m
<_sre.SRE_Match object; span=(5, 20), match='dave@google.com'>>>> text[m.start():m.end()]
'dave@google.com'

regex.match只在模式出现于字符串起始位置时进行匹配,如果没有匹配到,返回None:

>>> print(regex.match(text))
None

sub会返回一个新的字符串,原字符串中的模式会被一个新的字符串替代:

>>> print(regex.sub('REDACTED', text))
Dave REDACTED
Steve REDACTED
Rob REDACTED
Ryan REDACTED

假设想查找电子邮件地址,并将每个地址分为三个部分:用户名,域名和域名后缀。要实现这一点,可以用括号将模式包起来:

>>> pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
>>> regex = re.compile(pattern, flags=re.IGNORECASE)

由这个修改后的正则表达式产生的匹配对象的groups方法,返回的是模式组件的元组:

>>> m = regex.match('wesm@bright.net')
>>> m.groups()
('wesm', 'bright', 'net')

当模式可以分组时,findall返回的是包含元组的列表:

>>> regex.findall(text)
[('dave', 'google', 'com'),('steve', 'gmail', 'com'),('rob', 'gmail', 'com'),('ryan', 'yahoo', 'com')]

sub也可以使用特殊符号,如\1和\2,访问每个匹配对象中的分组。符号\1代表的是第一个匹配分组,\2代表的是第二个匹配分组,以此类推:

>>> print(regex.sub(r'Username: \1, Domain: \2, Suffix: \3', text))
Dave Username: dave, Domain: google, Suffix: com
Steve Username: steve, Domain: gmail, Suffix: com
Rob Username: rob, Domain: gmail, Suffix: com
Ryan Username: ryan, Domain: yahoo, Suffix: com

正则表达式方法

3.3 pandas中的向量化字符串函数

清理杂乱的数据集用于分析通常需要大量的字符串处理和正则化。包含字符串的列有时会含有缺失数据,使事情变得复杂:

>>> data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com',
>>>         'Rob': 'rob@gmail.com', 'Wes': np.nan}>>> data = pd.Series(data)
>>> data
Dave     dave@google.com
Rob        rob@gmail.com
Steve    steve@gmail.com
Wes                  NaN
dtype: object>>> data.isnull()
Dave     False
Rob      False
Steve    False
Wes       True
dtype: bool

可以使用data.map将字符串和有效的正则表达式方法(以lambda或其他函数的方式传递)应用到每个值上,但是在NA(null)值上会失败。为了解决这个问题,Series有面向数组的方法用于跳过NA值的字符串操作。这些方法通过Series的str属性进行调用,例如,可以通过str.contains来检查每个电子邮件地址是否含有'gmail':

>>> data.str.contains('gmail')
Dave     False
Rob       True
Steve     True
Wes        NaN
dtype: object

正则表达式也可以结合任意的re模块选项使用,例如IGNORECASE:

>>> pattern
'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'>>> x = data.str.findall(pattern, flags=re.IGNORECASE)
>>> x
Dave     [(dave, google, com)]
Rob        [(rob, gmail, com)]
Steve    [(steve, gmail, com)]
Wes                        NaN
dtype: object

有多种方法可以进行向量化的元素检索。可以使用str.get或在str属性内部索引:

>>> matches = data.str.match(pattern, flags=re.IGNORECASE)
>>> matches
Dave     True
Rob      True
Steve    True
Wes       NaN
dtype: object

要访问嵌入式列表中的元素,可以将索引传递给这些函数中的任意一个:

>>> x.str.get(1)
Dave    NaN
Rob     NaN
Steve   NaN
Wes     NaN
dtype: float64>>> x.str[0]
Dave    NaN
Rob     NaN
Steve   NaN
Wes     NaN
dtype: float64

可以使用字符串切片的类似语法进行向量化切片:

>>> data.str[:5]
Dave     dave@
Rob      rob@g
Steve    steve
Wes        NaN
dtype: object

部分向量化字符串方法列表

04 数据清洗与准备相关推荐

  1. python第三方库排行-140种Python标准库、第三方库和外部工具

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  2. python第三方库大全win-Python标准库、第三方库和外部工具汇总

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  3. 140种Python标准库、第三方库和外部工具都有了

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  4. 干货 | 数据分析的 7 个关键步骤是什么?

    "数据科学家" 这个名号总让人联想到一个孤独的天才独自工作,将深奥的公式应用于大量的数据,从而探索出有用的见解.但这仅仅是数据分析过程中的一步.数据分析本身不是目标,目标是使企业能 ...

  5. cassandra可视化工具_耗时1个月整理!160种Python标准库、第三方库和外部工具都有了...

    耗时1个月整理!160种Python标准库.第三方库和外部工具都有了 北京尚学堂 2019-12-09 14:59:15 Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数 ...

  6. [转载] 140种Python标准库、第三方库和外部工具

    参考链接: Python | 使用openpyxl在Excel文件中进行算术运算 这些库可用于文件读写.网络抓取和解析.数据连接.数清洗转换.数据计算和统计分析.图像和视频处理.音频处理.数据挖掘/机 ...

  7. Excel|5个神技巧,提高你的数据分析效率~

    对于刚进入数据分析行业新手来说,EXCEL可以被当做一款入门的软件.在学习R或Python前,事先掌握一定的EXCEL知识是百利而无一害.EXCEL凭借其功能强大的函数.可视化图表.以及整齐排列的电子 ...

  8. 【转】140种Python标准库、第三方库和外部工具

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  9. python库吐血整理

    Python的第三方库.这些库需要先进行安装(部分可能需要配置). 外部工具:非Python写成的库或包,用于Python数据工作的相关工具. 「推荐度」3星最高,1星最低. 01 文件读写 文件的读 ...

  10. 吐血整理!140种Python标准库、第三方库和外部工具都有了

    导读:大家好,我是涛哥.Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络 ...

最新文章

  1. (最简单)小米MIX 2S的usb调试模式在哪里开启的流程
  2. ubuntu设置jupyter
  3. Spring Data ElasticSearch示例--查询索引库
  4. mysql整站源码安装_MySQL入门01-MySQL源码安装
  5. 【Matlab】 读取文件各种方法
  6. Leetcode376.摆动序列
  7. JS版的bin2hex和hex2bin,支持汉字
  8. linux yast命令,Linux_SUSE Linux系统上双网卡绑定方法,1、使用yast工具配置第一块网 - phpStudy...
  9. 电影中的计算机 过去与未来
  10. excel取消隐藏_Excel表格如何快速隐藏并取消隐藏
  11. ipod nano7安装linux,怎么用ipod nano看视频?
  12. DNS是什么?DNS什么用?
  13. NOJ-1148-石子合并
  14. windows远程连接Ubuntu16.04桌面版,有界面链接和无界面链接两种方式
  15. Send Automatic Out Of Office replies from Outlook
  16. MacBook m1 芯片快速下载 ndk 和环境配置,防止脱发
  17. 理科男用这种方式泡妞,真的能迎娶白富美吗?!
  18. 什么是文件包含漏洞?文件包含漏洞分类!
  19. 计算机在职双证博士的学校,国内在职双证博士5所高校是哪些
  20. (一)图像文字检测论文:CTPN方法

热门文章

  1. Matlab之聚类分析
  2. 简单的贪吃蛇代码,可上机运行
  3. 搜c语言题答案软件下载,C语言二级题库软件下载-C语言二级题库官方版下载v1.01-乐游网安卓下载...
  4. 鲜为人知的合作:锐捷和juniper、联想和三茗
  5. 通信技术基础知识回顾
  6. 【MMD tools for bleander,Bleander的插件】
  7. 互联网网站采集工具大比拼和选择指南
  8. Go测试远控免杀学习
  9. Windows xp sp3 补丁下载-cuyahoga
  10. metasploit命令及模块