python-for-data-analysis_2nd 第七章 数据清洗和准备

文章目录

  • 第七章 数据清洗和准备
    • 7.1处理缺失数据
        • 1.常用方法
        • 2.滤除缺失数据(Filling In Missing Data)
        • 3.填充缺失数据(Filling In Missing Data)
    • 7.2 数据转换(Data Transformation)
        • 1.移除重复数据(Removing Duplicates)
        • 2.利⽤函数或映射进⾏数据转换(Transforming Data Using a Function or Mapping)
        • 3.替换值(Replacing Values)
        • 4.重命名轴索引(Renaming Axis Indexes)
        • 5. 离散化和面元划分(Discretization and Binning)
        • 6.检测和过滤异常值(Detecting and Filtering Outliers)
        • 7.排列和随机采样(Permutation and Random Sampling)
        • 8.计算指标/哑变量(Computing Indicator/Dummy Variables)
    • 7.3 字符串操作(String Manipulation)
        • 1.字符串对象方法(String Object Methods)
        • 2.正则表达式(Regular Expressions)
        • 3.pandas的⽮量化字符串函数(Vectorized String Functions in pandas)

第七章 数据清洗和准备

在数据分析和建模的过程,相当多的时间要⽤在数据准备上:加载、清理、转换以及重塑(load, clean up, transform, and reshape)。许多研究者都选择使⽤通⽤编程语⾔(如Python、Perl、R或Java)或UNIX⽂本处理⼯具(如sed或awk)对数据格式进⾏专⻔处理。但是pandas和内置的python标准库提供了一组高级的、灵活的、快速的工具,pandas中许多设计和实现都是由真实应用的需求所驱动的,通俗的讲就是pandas在应用于处理数据是非常有效的。

本章的重点:处理缺失数据、重复数据、字符串操作和其它分析数据转换的⼯具

7.1处理缺失数据

1.常用方法

方法 说明
dropna 去除表中缺失行或列
fillna 指定值填充
isnull 返回不为NA值的布尔值对象
notnull isnull的否定
string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data
string_data.isnull()
string_data.fillna(12)
#下面两者的区别是一样的
string_data.dropna()
string_data[string_data.notnull()]

可以通过pandas.isnull或布尔索引的⼿⼯⽅法,但dropna可能会更实⽤⼀些。

2.滤除缺失数据(Filling In Missing Data)

df = pd.DataFrame(np.random.randn(7, 3))
df.iloc[:4, 1] = NA
df.iloc[:2, 2] = NA
df.iloc[:3,0]=NA
df
df.dropna()
#thresh这个作用不大
df.dropna(thresh=2)

3.填充缺失数据(Filling In Missing Data)

不想滤除缺失数据(有可能会丢弃跟它有关的其他数据),⽽是希望通过其他⽅式填补那些“空洞”。

fillna()

df = pd.DataFrame(np.random.randn(6, 3))
df.iloc[2:, 1] = NA
df.iloc[4:, 2] = NA
df
#字典填充
df.fillna({
     1: 0.5, 2: 0})
#前向填充
df.fillna(method='ffill')
df.fillna(method='ffill', limit=2)	
#平均数填充
df.fillna(df.mean())

常用的参数:value、method、axis、inplace、limit

7.2 数据转换(Data Transformation)

1.移除重复数据(Removing Duplicates)

duplicated() 、drop_duplicates()

data = pd.DataFrame({
     'k1': ['one', 'two'] * 3 + ['two'],
                     'k2': [1, 1, 2, 3, 3, 4, 4]})
data
#返回bool值,重复为True;不重复为False
data.duplicated()
#在添一列
data['v1'] = range(7)
#去除重复列
data.drop_duplicates(['k1', 'k2'], keep='last')

2.利⽤函数或映射进⾏数据转换(Transforming Data Using a Function or Mapping)

map()

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]})
meat_to_animal = {
     
  'bacon': 'pig',
  'pulled pork': 'pig',
  'pastrami': 'cow',
  'corned beef': 'cow',
  'honey ham': 'pig',
  'nova lox': 'salmon'
}
lowercased = data['food'].str.lower()
data['animal'] = lowercased.map(meat_to_animal)
data
#下面这一种方式我不太明白
data['food'].map(lambda x: meat_to_animal[x.lower()])

思考:利用函数或映射可以进行数据的归类,然后在通过可视化展示(很好的方法)

使⽤map是⼀种实现元素级转换以及其他数据清理⼯作的便捷⽅式。

3.替换值(Replacing Values)

利⽤fillna⽅法填充缺失数据可以看做值替换的⼀种特殊情况,⽽replace则提供了⼀种实现该功能的更简单、更灵活的⽅式。

fillnareplace()

data = pd.Series([1., -999., 2., -999., -1000., 3.])
data
data.replace([-999, -1000], [np.nan, 0])
data.replace({
     -999: np.nan, -1000: 0})

都还可以用,我偏向于fillna

笔记:data.replace⽅法与data.str.replace不同,后者做的是字符串的元素级替换。我们会在后⾯学习Series的字符串⽅法。

4.重命名轴索引(Renaming Axis Indexes)

  • 跟Series⼀样,轴索引也有⼀个map⽅法
  • rename()
data = pd.DataFrame(np.arange(12).reshape((3, 4)),
                    index=['Ohio', 'Colorado', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data
transform = lambda x: x[:4].upper()
data.index.map(transform)
data.index = data.index.map(transform)
data
#index中首字母大写,其余小写;columns中字母大写
data.rename(index=str.title, columns=str.upper)
#index中重命名;columns中重命名
data.rename(index={
     'OHIO': 'INDIANA'},
            columns={
     'three': 'peekaboo'})

rename中的对横纵索引重命名非常重要的一个知识。

5. 离散化和面元划分(Discretization and Binning)

为了便于分析,连续数据常常被离散化或拆分为“⾯元”(bin)。假设有⼀组⼈员数据,⽽你希望将它们划分为不同的年龄组,这种例子在实际操作很常见。我这里会提供一个我之前做过的案例。

cut() 其方法有codes、categories等

cut() 其属性有right(若right=False,区间就为右开)、labels(传递⼀个列表或数组到labels,设置⾃⼰的⾯元名称)等

ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
cats=pd.cut(ages, [18, 26, 36, 61, 100], right=False,labels=['Youth', 'YoungAdult', 'MiddleAged', 'Senior'])
#cats
#cats.codes
#cats.categories
pd.value_counts(cats)
#上述代码就可以实现画图了

qcut()由于使⽤的是样本分位数,因此可以得到⼤⼩基本相等的⾯元。

data = np.random.randn(1000)  # Normally distributed
cats = pd.qcut(data, 4)  # Cut into quartiles
cats = pd.qcut(data, 4,precision=2)  
cats
pd.value_counts(cats)

讲解聚合和分组运算时会再次⽤到cut和qcut,因为这两个离散化函数对分位和分组分析⾮常重要。

同时这个离散化和面源切分(cut)也是绘制条形图最好用的方法。

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

data = pd.DataFrame(np.random.randn(1000, 4))
col = data[2]
#下面代码这种思想来detect
data[(np.abs(data) > 3).any(1)]
#np.sign(data)用法
data[np.abs(data) > 3] = np.sign(data) * 3
data.describe()

小案例:

import numpy as np
import pandas as pd

# 这里我们先随机生成300名学生的成绩
data = pd.DataFrame(np.random.randint(0,101,size = 300).reshape((300,4)),columns = ['A','B','C','D'])
data.iloc[[0,11,33,55,66,77],[0,1,2,3]] = np.random.randint(101,1000,size = 24).reshape((6,4))
error_A = data['A']
error_listA = error_A[(100 < error_A) | (error_A < 0)] 
print('\nerror_listA:')
print(error_listA)

error = data[((100 < data) | (data < 0)).any(1)]

print('\nerror:')
print(error)
# 假如我们还想要对这些成绩进行排名,比如排名A班的成绩。可以用下面方法实现
rank_list = data['A'].sort_values(axis=0,ascending=True)

异常值监测通常是去除不合实际的值。

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

利⽤numpy.random.permutation函数可以轻松实现对Series或DataFrame的列的排列⼯作(permuting,随机重排序)。通过需要排列的轴的⻓度调⽤permutation,可产⽣⼀个表示新顺序的整数数组。

numpy.random.permutation() 关键一点该方法是随机重排列

df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4)))
sampler = np.random.permutation(5)
sampler
df
sampler[2]
#基于iloc的索引操作或take函数中使⽤该数组
df.iloc[sampler[2]]
df.take(sampler)
#随机抽取12行.replace表示允许重复选择
df.sample(n=12,replace=True)

sample() 随机采样

choices = pd.Series([5, 7, -1, 6, 4])
choices
draws = choices.sample(n=10, replace=True)
draws

上述这两种方式都可以,但是我建议还是使用后者

replace表示允许重复选择

8.计算指标/哑变量(Computing Indicator/Dummy Variables)

常⽤于统计建模或机器学习的转换⽅式是:将分类变量(categorical variable)转换为**“哑变量”或“指标矩阵”**。

如果DataFrame的某⼀列中含有k个不同的值,则可以派⽣出⼀个k列矩阵或DataFrame(其值全为1和0)。pandas有⼀个get_dummies函数可以实现该功能。

get_dummies()

df = pd.DataFrame({
     'key': ['b', 'b', 'a', 'c', 'a', 'b'],
                   'data1': range(6)})
#属性prefix的作用是给指标DataFrame的列加上⼀个前缀
pd.get_dummies(df['key'],prefix='key')

df_with_dummy = df[['data1']].join(dummies)
df_with_dummy

这个方法也可以用来做条形图(仔细想一下)

在python中,关于list添加元素的操作有两个方法,即extend和append。但两者的用法还是存在一些区别:

  • append可以添加单个元素、可迭代对象,但是extend只能添加可迭代对象
  • 在添加可迭代对象是,append在添加后不改变添加项的类型;而extend在添加后,会将添加项进行迭代,迭代的元素挨个添加到被添加的数组中
import numpy as np 
import pandas as  pd 

mnames = ['movie_id', 'title', 'genres']
movies = pd.read_table('datasets/movielens/movies.dat', sep='::',\
                       header=None, names=mnames)
print(movies[:10])

all_genres = []
for x in movies.genres:
    all_genres.extend(x.split('|'))
genres = pd.unique(all_genres)
print(genres)

#构建指标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('|')
test=dummies.columns.get_indexer(gen.split('|'))

for i, gen in enumerate(movies.genres):
    ##获取dummies.columns中gen内容的索引位置
    indices = dummies.columns.get_indexer(gen.split('|'))
    dummies.iloc[i, indices] = 1

movies_windic = movies.join(dummies.add_prefix('Genre_'))
print(movies_windic.iloc[0])

这个代码写的非常美,值得回味。其中dummies.columns.get_indexer(gen.split('|'))要注意(获取dummies.columns中gen内容的索引位置)。

笔记:对于很⼤的数据,⽤这种⽅式构建多成员指标变量就会变得⾮常慢。最好使⽤更低级函数,将其写⼊NumPy数组,然后结果包装在DataFrame中。如何实现呢?不知道

下面一例就更有效果了:⼀个对统计应⽤有⽤的秘诀是:结合get_dummies和诸如cut之类的离散化函数。简单的说就是结合get_dummies(指标矩阵)和各种离散化函数(这里是最常用的cut)就可以做直方图。

np.random.seed(12345)
values = np.random.rand(10)
values
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
pd.get_dummies(pd.cut(values, bins))
#直接得到每个区间的指标矩阵,就可以在做统计画直方图了

本书后⾯会介绍pandas.get_dummies,只能说这种dummy variables在统计分析中真的是太重要了。

7.3 字符串操作(String Manipulation)

Python能够成为流⾏的数据处理语⾔,部分原因是其简单易⽤的字符串和⽂本处理功能。⼤部分⽂本运算都直接做成了字符串对象的内置⽅法

对于更为复杂的模式匹配和⽂本操作,则可能需要⽤到正则表达式。pandas对此进⾏了加强,它使你能够对整组数据应⽤字符串表达式和正则表达式,⽽且能处理烦⼈的缺失数据。

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

  • 以逗号分隔的字符串可以⽤split拆分成数段

  • split常常与strip⼀起使⽤,以去除空⽩符(包括换⾏符)

  • 子串定位:检测⼦串的最佳⽅式是利⽤Python的in关键字,还可以使⽤indexfind。注意find和index的区别:如果找不到字符串,index将会引发⼀个异常(⽽不是返回-1)

  • count可以返回指定⼦串的出现次数

  • replace⽤于将指定模式替换为另⼀个模式。

val = 'a,b,  guido'
val.split(',')
#`split`常常与`strip`⼀起使⽤,以去除空⽩符(包括换⾏符)
pieces = [x.strip() for x in val.split(',')]
pieces
#字符串以某种形式连接在一块
first, second, third = pieces
first + '::' + second + '::' + third
##字符串以某种形式连接在一块,更符合python的风格
'::'.join(pieces)
#——————————————————————————
#字串定位
'guido' in val
val.index(',')
val.find(':')
#——————————
val.replace(',', '::')
val.replace(',', '')
python-for-data-analysis_2nd 第七章 数据清洗和准备_第1张图片

casefold 将字符转换为⼩写,并将任何特定区域的变量字符组合转换成⼀个通⽤的可⽐较形式。

2.正则表达式(Regular Expressions)

正则表达式提供了⼀种灵活的在⽂本中搜索或匹配(通常⽐前者复杂)字符串模式的⽅式。

正则表达式,常称作regex,是根据正则表达式语⾔编写的字符串。Python内置的re模块负责对字符串应⽤正则表达式。

正则表达式的编写技巧内容很多,可以⾃成⼀章

re模块的函数可以分为三个⼤类:模式匹配、替换以及拆分。

import re
text = "foo    bar\t baz  \tqux"
re.split('\s+', text)
#可复用的regex对象
regex = re.compile('\s+')
regex.split(text)

打算对许多字符串应⽤同⼀条正则表达式,强烈建议通过re.compile创建regex对象。这样将可以节省⼤量的CPU时间。

#识别⼤部分电⼦邮件地址的正则表达式
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}'
# re.IGNORECASE makes the regex case-insensitive
regex = re.compile(pattern, flags=re.IGNORECASE)
regex.findall(text)
#search返回的是⽂本中第⼀个电⼦邮件地址(以特殊的匹配项对象形式返回)
m = regex.search(text)
m
text[m.start():m.end()]
#regex.match则将返回None,因为它只匹配出现在字符串开头的模式
print(regex.sub('REDACTED', text))
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
regex = re.compile(pattern, flags=re.IGNORECASE)
m = regex.match('[email protected]')
m.groups()

regex.findall(text)

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: comx
'''
python-for-data-analysis_2nd 第七章 数据清洗和准备_第2张图片

3.pandas的⽮量化字符串函数(Vectorized String Functions in pandas)

清理待分析的散乱数据时,常常需要做⼀些字符串规整化⼯作。更为复杂的情况是,含有字符串的列有时还含有缺失数据。

import re
data = {
     'Dave': '[email protected]', 'Steve': '[email protected]',
        'Rob': '[email protected]', 'Wes': np.nan}
data = pd.Series(data)
data
data.isnull()

data.str.contains('gmail')

pattern
data.str.findall(pattern, flags=re.IGNORECASE)

matches = data.str.match(pattern, flags=re.IGNORECASE)
matches

data.str[:4]

总结与收获:

  • 在数据处理中最后在设置索引列index_col,虽然你感觉有点乱,但是在实际操作中你会觉得很有好处。
  • 重要知识:计算指标/哑变量(Computing Indicator/Dummy Variables)。

你可能感兴趣的:(数据分析,数据挖掘,python,数据分析,大数据)