Pandas数据分析之数据清洗与准备

Pandas数据分析之数据清洗与准备


一、处理缺失值

Pandas使用浮点值 NaN(Not a Number)表示缺失数据,即 np.nan,通过isnull()判断为True。
缺失值表示为NA(Not available)表示不可用。
None被当作NA处理,通过isnull()判断为True

1、 NA处理方法

函数名描述
dropna根据每个标签的值中是否缺失数据来筛选轴标签,并根据允许丢失的数据量来确定阈值
fillna用指定值或插值方法(如ffill:前->后/上->下或bfill后->前/下->上)填充缺失数据
isnull返回表面哪些值是缺失值的布尔值
notnullisnull的反函数

(1)过滤缺失值

在Series中使用dropna,会返回Series中所有的非空数据及其索引

from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna() #与data[data.notnull()]等价

当处理DataFrame对象,dropna默认情况下删除包含缺失值的,axis=1则默认删除列
thresh=n,每行中需要保留的最小非缺失值数量为n,可以结合axis=1使用

data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],[NA, NA, NA], [NA, 6.5, 3.]])
cleaned = data.dropna() # 删除所有包含有NA的行
data.dropna(how='all') # 删除所有值均为NA的行
data[4] = NA
data.dropna(axis=1, how='all') #删除所有值均为NA的列
data.dropna(thresh=2) # 删除所有非NA个数小于2的行

(2)补全缺失值

df.fillna(0) #将0替代所有NA
df.fillna({1: 0.5, 2: 0}) #通过字典,不同列不同的填充值
temp = df.fillna(0, inplace=True) # 修改已存在对象,fillna默认返回新对象
df.fillna(method='ffill') # 向下填充,可与axis=1结合使用,bfill同理
df.fillna(method='ffill', limit=2) #限制填充个数

二、数据转换

1、删除重复值

data.duplicated() #返回一个布尔型Series,表示是否有重复行
data.drop_duplicates() #返回DataFrame,去掉重复行
data.drop_duplicates(['k1']) #去掉k1列的重复值,默认保留第一个重复值
data.drop_duplicates(['k1', 'k2'], keep='last') # 去掉k1和k2列的重复值,保留最后一个重复值

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]})
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) #map按元素转换

3、替代值

replace比map更灵活简洁

data = pd.Series([1., -999., 2., -999., -1000., 3.])
data.replace(-999, np.nan) # NA替换数据
data.replace([-999, -1000], np.nan) # NA替换多个数据
data.replace([-999, -1000], [np.nan, 0]) # 原数值和替换值对应
data.replace({-999: np.nan, -1000: 0}) #字典同上,DataFrame也可使用,替换不同列的不同值

4、重命名轴索引

data = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=['Ohio', 'Colorado', 'New York'],
columns=['one', 'two', 'three', 'four'])
transform = lambda x: x[:4].upper() #匿名函数
data.index = data.index.map(transform) # 会修改原数据
data.rename(index=str.title, columns=str.upper) #不修改原数据,返回新对象,行标签首字母大写,列标签所有字母大写
data.rename(index={'OHIO': 'INDIANA'},columns={'three': 'peekaboo'}) #结合字典使用
data.rename(index={'OHIO': 'INDIANA'}, inplace=True) # 修改原数据

5、离散化和分箱

ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins) # 特殊的Categorical对象,箱子区间左开又闭
"""
 cats.codes:ages对应的不同箱子的索引,即
 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.cut(ages, [18, 26, 36, 61, 100], right=False) # 改为左闭右开
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]
"""
pd.cut(data, 4, precision=2) # data分成4份,precision=2,小数只有两位,不一定每个箱子数量相等,取决于数据分布
cats = pd.qcut(data, 4) #使用样本的分位数,可以获得等长的箱子,均分
pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.]) #自定义箱子

6、检测和过滤异常值

只是简单的例子,异常检测方法需要具体问题具体分析

data = pd.DataFrame(np.random.randn(1000, 4)) #4个正态分布
data.describe() #数据集的基本统计信息,包括每个数值列的总数,平均值、标准差、最小值、第一四分位数、中位数、第三四分位数、最大值
col = data[2]
col[np.abs(col) > 3]
data[(np.abs(data) > 3).any(1)] # any(axis=1),列的方向,每行为单位,存在绝对值大于3保留
data[np.abs(data) > 3] = np.sign(data) * 3 #绝对值大于3的数替换为同符号的3或者-3
np.sign(data).head() #根据数值的正负生成1/-1,默认查看前5行

在这里插入图片描述

7、置换和随机采样

df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4)))
sampler = np.random.permutation(5) #返回一个随机排列的新对象,[0,5)
df.take(sampler) #按索引排列,默认为行,axis=1为列
df.sample(n=3) #指定采样个数,不能与frac采样比例同时使用,replace=True,有放回的采样,可重复

8、计算指标/虚拟变量

np.random.seed(12345)
values = np.random.rand(10)
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
pd.get_dummies(pd.cut(values, bins)) #get_dummies生成独热编码

三、字符串操作

1、字符串对象方法

val = 'a,b, guido'
pieces = [x.strip() for x in val.split(',')] # 先去除逗号后去除空格,包括换行
'::'.join(pieces) # 字符串链接
'guido' in val #检测子串
val.index(',') # 没找到会抛出异常
val.find(':') # 没找到返回-1
val.count(',') #返回子串出现次数
方法描述
count返回子串在字符串中的非重叠出现次数
endswith如果字符串以某个后缀结尾,则返回True
startswith如果字符串以某个前缀开始,则返回True
join字符串作为连接其他字符串序列的分隔符
index如果在字符串中找到子串,则返回子串第一个字符所在的位置。反之引发ValueError
find如果在字符串中找到子串,则返回第一次发现的子串的第一个字符所在的位置,反之返回-1
rfind如果在字符串中找到子串,则返回最后一次发现的子串的第一个字符所在的位置,反之返回-1
replace使用一个字符串替换指定子串
strip、rstrip、 lstrip去除两边/左边/右边空白符(包括换行符)
split指定分隔符将字符串拆分为子字符串的列表
lower大写字母转小写字母
upper小写字母转大写字母
casefold将字符串转小写,可以处理特殊字符
ljust、 rjust左对齐或者右对齐,用空格或其他字符填充字符串空白侧以返回符合最低宽度的字符串

2、正则表达式

Python内置的re模块负责对字符串应用正则表达式
re模块的函数可以分为三个大类:模式匹配、替换以及拆分
如果想避免转义字符\的影响,使用原始字符串语法,如r'C:\x''C:\\x'等价

import re
text = "foo bar\t baz \tqux"
re.split('\s+', text) # ['foo', 'bar', 'baz', 'qux'] '\s+'一个或者多个空白字符
regex = re.compile('\s+') #可复用的正则表达式对象,推荐使用,有利于节约CPU周期
regex.split(text) # 去掉空白字符生成列表 ['foo', 'bar', 'baz', 'qux']
regex.findall(text) #匹配空白字符生成列表,匹配所有项  [' ', '\t ', ' \t']
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) 
regex.findall(text) #返回匹配到的所有
regex.search(text) #返回匹配到的第一个
regex.match(text) #只在字符串起始位置时进行匹配,若没匹配到返回None
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('wesm@bright.net')
m.groups() #  ('wesm', 'bright', 'net')
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
"""
方法描述
findall将字符串中所有的非重叠匹配模式以列表形式返回
finditer与findall类似,返回迭代器
match在字符串起始位置匹配模式,如果匹配到模式,则返回一个匹配项对象,否则返回None
search扫描整个字符串以匹配模式,可以返回字符串任意位置,而不仅仅是字符串的起始位置
split根据模式将字符串拆分为多个部分
sub、subn替换字符串中所有的匹配(sub)或第n个出现的匹配串(subn),使用符合\1,\2等等来引用替换字符串中匹配组元素

3、向量化字符串函数

data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com', 'Rob':'rob@gmail.com', 'Wes': np.nan}
data = pd.Series(data)
data.str.contains('gmail') # 检查电子邮件是否含有gmail
pattern = '([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'
matches = data.str.match(pattern, flags=re.IGNORECASE)
"""
	Dave True
	Rob True
	Steve True
	Wes NaN
	dtype: object
"""
matches.str.get(1) #内部属性索引,似乎有点问题
matches.str[0]
data.str[:5] #切片
方法描述
cat根据可选的分隔符按元素粘合字符串
contains返回是否含有某个模式/正则表达式的布尔值数组
count模式出现次数的计数
extract使用正则表达式从Series字符串中分组抽取一个或多个字符串,返回的结果是每个分组形成的一列DataFrame
endswith等价于对每个元素使用x.endswith(模式)
startswith等价于对每个元素使用x.startswith(模式)
findall找出字符串中所有的模式/正则表达式匹配项,返回列表
get获得第i个元素
isalnum等价于内建str.alnum,判断英文字母和数字
isalpha等价于内建str.isalpha,判断英文字母
isdecimal等价于内建str.isdecimal
isdigit等价于内建str.isdigit
islower等价于内建str.islower
isnumeric等价于内建str.isnumeric
isupper等价于内建str.isupper
join根据传递的分隔符,将Series的字符串联合
len计算每个字符串的长度
lower, upper转换大小写,等价于对每个元素使用x.lower()或者x.upper()
match使用re.match将正则表达式应用到每个元素上,将匹配分组以列表形式返回
pad将空白加到字符串的两边/左边/右边
center等价于pad(side=‘both’)
repeat重复值,s.str.repeat(3)等价于对每个字符串x*3
replace以其他字符串替代模式/正则表达式的匹配项
slice对Series中的字符串进行切片
split以分隔符或正则表达式对字符串进行拆分
strip对字符串的两侧的空白进行消除,包括换行符
rstrip消除字符串右边的空白
lstrip消除字符串左边的空白

总结

Pandas功能强大,这仅仅是一部分,学习任重而道远

参考文献

《利用Python进行数据分析》原书第二版中译
python-pandas.DataFrame向下向上填充,fillna和ffill的方法:https://blog.csdn.net/weixin_46089741/article/details/122305108
fillna函数参数中limit参数:https://blog.csdn.net/2301_76287968/article/details/136169234
数据异常如何检验?本文给出14种检验方法!:https://mp.weixin.qq.com/s?__biz=MzI1MjQ2OTQ3Ng==&mid=2247632792&idx=1&sn=c62e8ab63b854b9cdefeb6713c86557e&chksm=e8b402b56d98f5731e0fe5057320aa0340f33fc2871e4545cea3feb86f2139c466f0779abd79&scene=27
df.sample() 随机取样:https://www.cnblogs.com/Teyisang/p/14564059.html
Pandas中的get_dummies()函数实战应用详解:https://blog.csdn.net/weixin_43856625/article/details/138792426

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值