利用Python进行数据分析笔记-数据清洗

在pandas中,missing data呈现的方式有些缺点的,但对大部分用户能起到足够的效果。对于数值型数据,pandas用浮点值Nan(Not a Number)来表示缺失值。我们称之为识别符(sentinel value),这种值能被轻易检测到 # 数据缺失 在pandas中,我们使用了R语言中的一些传统,把缺失值表示为NA(not available)。在统计应用里,NA数据别是要么是数据不存在,要么是存在但不能被检测到。做数据清理的时候,对缺失值做分析是很重要的,我们要确定是否是数据收集的问题,或者缺失值是否会带来潜在的偏见。 内建的Python None值也被当做NA
import pandas as pd
import numpy as np
from numpy import nan as NA

string_data = pd.Series(['aardvark', 'artichoke', NA, '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 这里有一些用来处理缺失值的函数: ![](http://oydgk2hgw.bkt.clouddn.com/pydata-book/zq0q8.png) ### 1、Filtering Out Missing Data(过滤缺失值) 有一些方法来过滤缺失值。可以使用pandas.isnull和boolean indexing, 配合使用dropna。对于series,只会返回non-null数据和index values:
# 直接过滤缺失值
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna()
0    1.0
2    3.5
4    7.0
dtype: float64
df = pd.DataFrame(np.random.randn(7, 3))
df
0 1 2
0 -0.029785 0.110282 -1.577633
1 -1.489474 -1.467383 0.006396
2 -1.105860 0.476798 -0.400001
3 -1.573019 0.147722 0.250568
4 -0.848726 1.002656 0.568422
5 1.457525 0.411282 -0.257635
6 2.828644 -1.237598 -0.532588
df.iloc[:4, 1] = NA
df.iloc[:2, 2] = NA
df
0 1 2
0 -0.029785 NaN NaN
1 -1.489474 NaN NaN
2 -1.105860 NaN -0.400001
3 -1.573019 NaN 0.250568
4 -0.848726 1.002656 0.568422
5 1.457525 0.411282 -0.257635
6 2.828644 -1.237598 -0.532588
# 会删除行或列
df.dropna()
0 1 2
4 -0.848726 1.002656 0.568422
5 1.457525 0.411282 -0.257635
6 2.828644 -1.237598 -0.532588
df.dropna(thresh=2) 
0 1 2
2 -1.105860 NaN -0.400001
3 -1.573019 NaN 0.250568
4 -0.848726 1.002656 0.568422
5 1.457525 0.411282 -0.257635
6 2.828644 -1.237598 -0.532588

2、Filling In Missing Data(填补缺失值)

在处理缺失值的时候,一些情况下我们会直接用dropna来把缺失值删除,但另一些情况下,我们希望用一些固定的值来代替缺失值,而fillna就是用来做这个的,例如,这里我们用平均值mean来代替缺失值NA
- fillna返回一个新对象,但你可以使用in-place来直接更改原有的数据

# 全部填充0
df.fillna(0)
0 1 2
0 -0.029785 0.000000 0.000000
1 -1.489474 0.000000 0.000000
2 -1.105860 0.000000 -0.400001
3 -1.573019 0.000000 0.250568
4 -0.848726 1.002656 0.568422
5 1.457525 0.411282 -0.257635
6 2.828644 -1.237598 -0.532588
# 给fillna传入一个dict,可以给不同列替换不同的值
df.fillna({1: 0.5, 2: 0})
0 1 2
0 -0.029785 0.500000 0.000000
1 -1.489474 0.500000 0.000000
2 -1.105860 0.500000 -0.400001
3 -1.573019 0.500000 0.250568
4 -0.848726 1.002656 0.568422
5 1.457525 0.411282 -0.257635
6 2.828644 -1.237598 -0.532588

下面是fillna的一些参数:

states = ['Ohio', 'New York', 'Vermont', 'Florida',
          'Oregon', 'Nevada', 'California', 'Idaho']

data = pd.Series(np.random.randn(8), index=states)
data[['Vermont', 'Nevada', 'Idaho']] = np.nan
data
Ohio          0.760579
New York      0.729209
Vermont            NaN
Florida      -2.195910
Oregon       -0.658126
Nevada             NaN
California   -1.851410
Idaho              NaN
dtype: float64
group_key = ['East'] * 4 + ['West'] * 4

data.groupby(group_key).mean()
East   -0.235374
West   -1.254768
dtype: float64

然后我们可以用每个组的平均值来填充NA:

fill_mean = lambda g: g.fillna(g.mean())
data.groupby(group_key).apply(fill_mean)
Ohio          0.760579
New York      0.729209
Vermont      -0.235374
Florida      -2.195910
Oregon       -0.658126
Nevada       -1.254768
California   -1.851410
Idaho        -1.254768
dtype: float64

在另外一些情况下,我们可能希望提前设定好用于不同组的填充值。因为group有一个name属性,我们可以利用这个:

fill_values = {'East': 0.5, 'West': -1}
fill_func = lambda g: g.fillna(fill_values[g.name])
data.groupby(group_key).apply(fill_func)
Ohio          0.760579
New York      0.729209
Vermont       0.500000
Florida      -2.195910
Oregon       -0.658126
Nevada       -1.000000
California   -1.851410
Idaho        -1.000000
dtype: float64

数据变换

1、删除重复值

data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
                     'k2': [1, 1, 2, 3, 3, 4, 4]})
data
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
6 two 4
# 判断是否有重复值
data.duplicated()
0    False
1    False
2    False
3    False
4    False
5    False
6     True
dtype: bool
# 删除重复值,只考虑k2列
data.drop_duplicates(['k2'])
k1 k2
0 one 1
2 one 2
3 two 3
5 two 4

2、用函数和映射来转换数据

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]})
data
food 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
# 大写字母转为小写字母
lowercased = data['food'].str.lower()   # upper() 小写字母转为大写字母
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
meat_to_animal = {
    'bacon': 'pig',
    'pulled pork': 'pig',
    'pastrami': 'cow',
    'corned beef': 'cow',
    'honey ham': 'pig',
    'nova lox': 'salmon'
}

data['animal'] = lowercased.map(meat_to_animal)
data
food 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
### 3、Replacing Values(替换值) 其实fillna是一个特殊换的替换操作。map可以用于修改一个object里的部分值,但是replace能提供一个更简单和更灵活的方法做到这点 - 用NA来替代的话,用replace,会产生一个新series(除非使用inplace=True)
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
# 一次替换多个值
data.replace([-999,-1000], np.nan)    # 用NaN替换成-999, -1000
0    1.0
1    NaN
2    2.0
3    NaN
4    NaN
5    3.0
dtype: float64
# 分开替换  方法一  用list
data.replace([-999,-1000], [-99,-100])  # -999,-1000分别替换为-99,-100
0      1.0
1    -99.0
2      2.0
3    -99.0
4   -100.0
5      3.0
dtype: float64
# 分开替换 方法二   用dict
data.replace({-999: np.nan, -1000: 0})  # -999,-1000分别替换为NaN,0
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

注意:data.replace方法和data.str.replace方法是不同的,后者会对string进行element-wise替换。

4、Renaming Axis Indexes(重命名Axis Indexes)

像是series里的value一样,axis label也能类似地是函数或映射来转换,产生一个新的object。当然也可以设置in-place不产生新的数据:

data = pd.DataFrame(np.arange(12).reshape((3, 4)),
                    index=['Ohio', 'Colorado', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
New York 8 9 10 11
data.index
Index(['Ohio', 'Colorado', 'New York'], dtype='object')
# 先小写转大写再截取前4个字母
transform = lambda x: x[:4].upper()  
# 不会修改原数据,会新建object
data.index.map(transform)
Index(['OHIO', 'COLO', 'NEW '], dtype='object')
# 直接赋值,会修改原数据
data.index = data.index.map(transform)
data
one two three four
OHIO 0 1 2 3
COLO 4 5 6 7
NEW 8 9 10 11

如果你想要创建一个转换后的版本,而且不用修改原始的数据,可以用rename

data.rename(index=str.title, columns=str.upper)
ONE TWO THREE FOUR
Ohio 0 1 2 3
Colo 4 5 6 7
New 8 9 10 11
# rename能用于dict一样的oject
data.rename(index={'OHIO': 'INDIANA'},
            columns={'three': 'pekaboo'}, 
            inplace=True)  # inplace=True 会修改原数据
data
one two pekaboo four
INDIANA 0 1 2 3
COLO 4 5 6 7
NEW 8 9 10 11

5、Discretization and Binning(离散化和装箱)

连续型数据经常被离散化或分散成bins(分箱)来分析。假设你有一组数据,你想把人分到不同的年龄组里,可以用pandas里的cut。

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

# 分到四个bin里,19~25, 26~35, 36~60, >60
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]]

返回的是一个特殊的Categorical object。我们看到的结果描述了pandas.cut如何得到bins。可以看作是一个string数组用来表示bin的名字,它内部包含了一个categories数组,用来记录不同类别的名字,并伴有表示ages的label(可以通过codes属性查看)

# 判断各区间里 ages 个数
cats.codes
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
# 查看区间  右关闭,默认right=True
cats.categories   # 注意区间。括号表示不包含,方括号表示包含。你可以自己设定哪一边关闭(right=False)
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]]
              closed='right',
              dtype='interval[int64]')
# 查看各区间的个数
pd.value_counts(cats)   # 这里pd.value_counts(cats)是pandas.cut后bin的数量
(18, 25]     5
(35, 60]     3
(25, 35]     3
(60, 100]    1
dtype: int64

这里pd.value_counts(cats)是pandas.cut后bin的数量。

这里我们注意一下区间。括号表示不包含,方括号表示包含。你可以自己设定哪一边关闭(right=False)

# 查看区间   左关闭 right=False
cuts2 = pd.cut(ages, [18, 26, 36, 61, 100], right=False)
cuts2.categories
IntervalIndex([[18, 26), [26, 36), [36, 61), [61, 100)]
              closed='left',
              dtype='interval[int64]')
# 用一个list或数组给labels选项来设定bin的名字
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)
[Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, MiddleAged, YoungAdult]
Length: 12
Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]

如果你只是给一个bins的数量来cut,而不是自己设定每个bind的范围,cut会根据最大值和最小值来计算等长的bins。比如下面我们想要做一个均匀分布的四个bins

data = np.random.rand(20)
pd.cut(data, 4, precision=2)  # precision=2 表示精确到小数点后两位
[(0.51, 0.72], (0.092, 0.3], (0.092, 0.3], (0.092, 0.3], (0.3, 0.51], ..., (0.092, 0.3], (0.3, 0.51], (0.092, 0.3], (0.51, 0.72], (0.3, 0.51]]
Length: 20
Categories (4, interval[float64]): [(0.092, 0.3] < (0.3, 0.51] < (0.51, 0.72] < (0.72, 0.93]]

一个近似的函数,qcut,会按照数据的分位数来分箱。取决于数据的分布,用cut通常不能保证每一个bin有一个相同数量的数据点。而qcut是按百分比来切的,所以可以得到等数量的bins

# 平均分为四部分
data = np.random.randn(1000)
cats = pd.qcut(data, 4) # 分四段
cats
[(-0.657, 0.0235], (0.0235, 0.685], (0.0235, 0.685], (-0.657, 0.0235], (-3.002, -0.657], ..., (0.0235, 0.685], (-0.657, 0.0235], (0.0235, 0.685], (0.685, 3.274], (-0.657, 0.0235]]
Length: 1000
Categories (4, interval[float64]): [(-3.002, -0.657] < (-0.657, 0.0235] < (0.0235, 0.685] < (0.685, 3.274]]
# 按百分比来切
pd.value_counts(cats)
(0.685, 3.274]      250
(0.0235, 0.685]     250
(-0.657, 0.0235]    250
(-3.002, -0.657]    250
dtype: int64

类似的,在cut中我们可以自己指定百分比:

cats2 = pd.cut(data, [0, 0.1, 0.5, 0.9, 1.]) # 累进的百分比
cats2
[NaN, (0.1, 0.5], (0.5, 0.9], NaN, NaN, ..., (0.1, 0.5], NaN, (0.1, 0.5], NaN, NaN]
Length: 1000
Categories (4, interval[float64]): [(0.0, 0.1] < (0.1, 0.5] < (0.5, 0.9] < (0.9, 1.0]]
pd.value_counts(cats2)
(0.1, 0.5]    163
(0.5, 0.9]    124
(0.0, 0.1]     37
(0.9, 1.0]     25
dtype: int64

6、Detecting and Filtering Outliers(检测和过滤异常值)

过滤或转换异常值是数组操作的一个重头戏。

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.005916 0.009086 -0.083772 -0.013680
std 0.982372 0.989198 0.994994 1.012198
min -3.317565 -2.773347 -3.306008 -3.563968
25% -0.695527 -0.693453 -0.734059 -0.689416
50% 0.015663 0.015709 -0.055282 -0.023872
75% 0.716060 0.674484 0.582683 0.670211
max 2.779288 3.222485 3.267874 3.328084
# 查看前5行
data.head()
0 1 2 3
0 0.112181 0.066283 2.693501 -1.098930
1 1.266445 0.661368 0.365074 0.151563
2 -0.895071 0.683068 0.100348 -0.804245
3 0.423192 -1.269358 0.309651 -0.204646
4 0.596470 1.592684 -0.032159 0.230689
# 查看列名为2的数据,并返回后5行
col = data[2]
col.tail()
995   -0.765241
996    1.399000
997    0.833521
998    0.465798
999    0.433316
Name: 2, dtype: float64
# 返回col中绝对值大于3的数
col[np.abs(col)> 3]
33     3.267874
111   -3.296620
561   -3.226878
666    3.230451
816   -3.306008
Name: 2, dtype: float64
# 所有绝对值大于3的行
# data[(np.abs(data) > 3).any(axis=1)] # any中axis=1表示column
data[(np.abs(data) > 3).any(1)]  # 同上
0 1 2 3
33 -0.294413 1.009243 3.267874 0.107169
99 1.957645 1.777069 1.643548 3.328084
111 -0.678862 -1.248394 -3.296620 -1.639195
147 0.332718 3.222485 -1.043792 0.435667
409 0.031337 1.574926 1.419990 3.323908
515 -1.660116 0.899630 -0.061961 3.030211
561 0.926034 1.035953 -3.226878 1.315150
656 0.637542 -1.059537 -1.769527 -3.165496
666 -1.339547 1.036912 3.230451 0.422901
676 0.104951 2.117886 0.404746 3.296446
684 -1.398323 -0.342887 0.580598 -3.020550
816 0.248030 -1.278687 -3.306008 1.844911
860 -3.317565 2.252828 0.865087 0.037816
865 1.146137 0.212220 1.081564 -3.563968
# 把绝对值大于3的数字直接变成-3或3
data[np.abs(data)>3] = np.sign(data) * 3    # np.sign(data)会根据值的正负号来得到1或-1
# 查看33行
data[33:34]
0 1 2 3
33 -0.294413 1.009243 3.0 0.107169

7、Permutation and Random Sampling(随机排列和随机采样)¶

排列(随机排序)一个series或DataFrame中的row,用numpy.random.permutation函数很容易就能做到。调用permutation的时候设定好你想要进行排列的axis,会产生一个整数数组表示新的顺序

df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4)), columns=list('ABCD'))
df
A B C D
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
# 随机排序
sampler = np.random.permutation(5)
sampler
array([0, 2, 4, 1, 3])
# 利用take()应用行索引
df.take(sampler)  # 默认axis=0, 如果axis=1将应用于列索引
A B C D
0 0 1 2 3
2 8 9 10 11
4 16 17 18 19
1 4 5 6 7
3 12 13 14 15
# 随机抽取3列数据
df.sample(3, axis='columns')   # 默认axis=0 表示行
D B A
0 3 1 0
1 7 5 4
2 11 9 8
3 15 13 12
4 19 17 16

8、Computing Indicator/Dummy Variables(计算指示器/虚拟变量)

Dummy Variables:虚拟变量,又称虚设变量、名义变量或哑变量,用以反映质的属性的一个人工变量,是量化了的自变量,通常取值为0或1。

另一种在统计模型上的转换或机器学习应用是把一个categorical variable(类别变量)变为一个dummy or indicator matrix(虚拟或指示器矩阵)。如果DataFrame中的一列有k个不同的值,我们可以用一个矩阵或DataFrame用k列来表示,1或0。pandas有一个get_dummies函数实现这个工作

df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
                   'data1': range(6)})
df
data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 b
# 通过get_dummies进行one-hot编码,产生虚拟变量表
pd.get_dummies(df['key'], prefix='key')  # prefix表示前缀
key_a key_b key_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
np.random.seed(12345)  # 伪随机数
values = np.random.rand(10)
values
array([0.92961609, 0.31637555, 0.18391881, 0.20456028, 0.56772503,
       0.5955447 , 0.96451452, 0.6531771 , 0.74890664, 0.65356987])
# 离散化和装箱
bins = [0, 0.2, 0.4, 0.6, 0.8, 1.]
pd.cut(values, bins)
[(0.8, 1.0], (0.2, 0.4], (0.0, 0.2], (0.2, 0.4], (0.4, 0.6], (0.4, 0.6], (0.8, 1.0], (0.6, 0.8], (0.6, 0.8], (0.6, 0.8]]
Categories (5, interval[float64]): [(0.0, 0.2] < (0.2, 0.4] < (0.4, 0.6] < (0.6, 0.8] < (0.8, 1.0]]
# 结合使用
pd.get_dummies(pd.cut(values, bins), prefix='区间')
区间_(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

字符串处理

python很多内建方法很适合处理string。而且对于更复杂的模式,可以配合使用正则表达式。而pandas则混合了两种方式。

1、String Object Methods(字符串对象方法)

大部分string处理,使用内建的一些方法就足够了。比如,可以用split来分割用逗号区分的字符串:

# 分离及去除空格(包括换行符)
val = 'a ,b, guido'
pieces = [x.strip() for x in val.split(',')]
pieces
['a', 'b', 'guido']
print('判断guido是否在这个字符串中:', 'guido' in val)
print('从第三个位置开始查找逗号索引值:', val.index(',', 3,))   # strs.index(str, beg=0, end=len(string))
print('查看字母"i"索引值:', val.find('i')  )                   # str.find(str, beg=0, end=len(string))

print('统计逗号个数:', val.count(','))
判断guido是否在这个字符串中: True
从第三个位置开始查找逗号索引值: 4
查看字母"i"索引值: 8
统计逗号个数: 2

注意index和find的区别。如果要找的string不存在的话,index会报错。而find会返回-1

# 合并
first, second, third = pieces
' & '.join(pieces)
'a & b & guido'
# 替换
val.replace(',', '::') 
'a ::b:: guido'

一些内建的string方法:

2、Regular Expressions(正则表达式)

正则表达式能让我们寻找更复杂的pattern。通常称一个表达式为regex,由正则表达语言来代表一个字符串模式。可以使用python内建的re模块来使用。

关于正则表达式,有很多教学资源,可以自己找几篇来学一些,这里不会介绍太多。

re模块有以下三个类别:patther matching(模式匹配), substitution(替换), splitting(分割)。通常这三种都是相关的,一个regex用来描述一种pattern,这样会有很多种用法。这里举个例子,假设我们想要根据空格(tabs,spaces,newlines)来分割一个字符串。

import re

# 用于描述一个或多个空格的regex是\s+
text = "foo    bar\t baz  \tqux"
re.split('\s+', text)
['foo', 'bar', 'baz', 'qux']
# 如果应用多次,这样分开能节省CPU的资源
regex = re.compile('\s+')
regex.split(text)
['foo', 'bar', 'baz', 'qux']
text = """Dave [email protected] 
          Steve [email protected] 
          Rob [email protected] 
          Ryan [email protected] """
pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'
regex = re.compile(pattern, flags=re.IGNORECASE)   # re.IGNORECASE表示区分大小写
# findall() 返回所有匹配的结果
regex.findall(text)
['[email protected]', '[email protected]', '[email protected]', '[email protected]']
# search() 只返回第一次查询到的结果
regex.search(text)
<_sre.SRE_Match object; span=(5, 20), match='[email protected]'>
# regex.match返回None,因为它只会在pattern存在于stirng开头的情况下才会返回匹配结果
print(regex.match(text))
None
# sub返回一个新的string,把pattern出现的地方替换为我们指定的string
print(regex.sub('REDACTED', text))
Dave REDACTED 
          Steve REDACTED 
          Rob REDACTED 
          Ryan REDACTED 
# 去空格和标点符号
def clean_string(strings):
    result = []
    for value in strings:
        value = value.strip()  # 删除首尾空格符及换行符
        value = re.sub('[!#?]', '', value)  # 利用正则表达式删除
        value = value.title()
        result.append(value)
    return result

states = [' Alabama ', ' Georgia!', 'Georgia ', 'georgia', 
          'FlOrIda', 'south carolina##', 'West virginia?']
clean_string(states)
['Alabama',
 'Georgia',
 'Georgia',
 'Georgia',
 'Florida',
 'South Carolina',
 'West Virginia']

这里给一些正则表达式的方法:

3、Vectorized String Functions in pandas(pandas中的字符串向量化函数)

一些复杂的数据清理中,string会有缺失值:

data = {'Dave': '[email protected]', 'Steve': '[email protected]', 
        'Rob': '[email protected]', 'Wes': np.nan}
data = pd.Series(data)
data
Dave     [email protected]
Rob        [email protected]
Steve    [email protected]
Wes                  NaN
dtype: object
# 是否有空值
data.isnull()
Dave     False
Rob      False
Steve    False
Wes       True
dtype: bool
# 是否包含字符串gmail
data.str.contains('gmail')
Dave     False
Rob       True
Steve     True
Wes        NaN
dtype: object
pattern = '([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'
data.str.findall(pattern, flags=re.IGNORECASE)
Dave     [(dave, google, com)]
Rob        [(rob, gmail, com)]
Steve    [(steve, gmail, com)]
Wes                        NaN
dtype: object

有很多方法用于向量化。比如str.get或index索引到str属性

data.str.get(1)
Dave       a
Rob        o
Steve      t
Wes      NaN
dtype: object
data.str[:5]
Dave     dave@
Rob      rob@g
Steve    steve
Wes        NaN
dtype: object

这里有一些字符串向量化的方法:

你可能感兴趣的:(数据分析,利用Python进行数据分析)