Python数据分析
Posted 雨宙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python数据分析相关的知识,希望对你有一定的参考价值。
Python数据分析(四)
打卡第八天啦!!!
pandas库(四)
数据加载、存储与文件格式
读写文本格式文件
- 直接使用read_csv方法进行读取
df = pd.read_csv('ex.csv')
- 使用read_table方法进行读取,需要指定分隔符
pd.read_table('ex.csv',sep=',')
- 在读取时可以加上列名,可以使用默认列名或自定义列名
pd.read_csv('ex2.csv',header=None) # 使用默认列名
pd.read_csv('ex2.csv',names=['a','b','c','d','message']) # 使用自定义列名
- 指定index_col属性,将某一列作为索引列
df1 = pd.read_csv('ex2.csv',names=['a','b','c','d','message'],index_col='message') # 将message列作为索引列
print(df1)
# a b c d
# message
# hello 1 2 3 4
# world 5 6 7 8
# python 9 10 11 12
- 指定index_col参数,将多列作为索引列(效果为修改为层次索引)
pa = pd.read_csv('ex3.csv',index_col=['key1','key2'])
print(pa)
# value1 value2
# key1 key2
# one a 1 2
# b 3 4
# c 5 6
# d 7 8
# two a 9 10
# b 11 12
# c 13 14
# d 15 16
- 读取txt文本文件,需要指定sep参数,不指定默认分隔符是\\t,不能处理换行符
df2 = pd.read_table('ex4.txt',sep='\\s+')
- 将数据写入csv文件
df3.to_csv('ex_out.csv')
- json数据与Python格式数据的相互转换
# json数据
obj = """
"paramz":
"feeds": [
"id": 299076,
"oid": 288340,
"category": "article",
"data":
"subject": "荔枝新闻3.0:不止是阅读",
"summary": "江苏广电旗下资讯类手机应用“荔枝新闻”于近期推出全新升级换代的3.0版。",
"cover": "/Attachs/Article/288340/3e8e2c397c70469f8845fad73aa38165_padmini.JPG",
"pic": "",
"format": "txt",
"changed": "2015-09-22 16:01:41"
],
"PageIndex": 1,
"PageSize": 20,
"TotalCount": 53521,
"TotalPage": 2677
"""
# 将json字符串转换为Python形式
import json
res = json.loads(obj)
res
# 'paramz': 'feeds': ['id': 299076,
# 'oid': 288340,
# 'category': 'article',
# 'data': 'subject': '荔枝新闻3.0:不止是阅读',
# 'summary': '江苏广电旗下资讯类手机应用“荔枝新闻”于近期推出全新升级换代的3.0版。',
# 'cover': '/Attachs/Article/288340/3e8e2c397c70469f8845fad73aa38165_padmini.JPG',
# 'pic': '',
# 'format': 'txt',
# 'changed': '2015-09-22 16:01:41'],
# 'PageIndex': 1,
# 'PageSize': 20,
# 'TotalCount': 53521,
# 'TotalPage': 2677
# 将Python对象转换为json字符串
res1 = json.dumps(res)
- 分块读取大文件,指定chunksize参数或iterator参数
# 分块读取大文件
# chunksize
agg1 = pd.read_csv('ex.csv',chunksize=10) # 一次读取10行
print(agg1.get_chunk())
# a b c d message
# 0 1 2 3 4 hello
# 1 5 6 7 8 world
# 2 9 10 11 12 python
agg1 = pd.read_csv('ex.csv',iterator=True)
agg1
# <pandas.io.parsers.TextFileReader at 0x1f2284e92e0>
agg1.get_chunk(2) # 读取前两行
数据数据清洗和准备
处理缺失数据
- 检测缺失数据,None和nan都能被检测
data1 = pd.Series(['a','b',np.nan,'d'])
# 检测缺失值 None和nan都能被检测
data1.isnull()
# 0 False
# 1 False
# 2 True
# 3 False
# dtype: bool
- 滤除缺失值
(1)在Series中滤除缺失值
data1.dropna()
# 0 a
# 1 b
# 3 d
# dtype: object
data1[data1.notnull()]
# 0 a
# 1 b
# 3 d
# dtype: object
(2)在DataFrame中滤除缺失值
data2.dropna() # 删去所有含nan的行
data2.dropna(how='all') # 删去全为nan的行
data2.dropna(axis=1) # 删去所有含nan的列
data2.dropna(axis=1,how='all') # 删去全为nan的列
在以上代码中,需要注意的是,若一行中出现nan,则方法dropna能直接删除这一行,但若在其中指定how参数为all,则不一定能删除此行,此时只有在此行都为nan的情况下才能删除此行
# 创建新的dataframe作为例子
df = pd.DataFrame(np.random.randn(7,3))
df.iloc[:4,1] = np.nan
df.iloc[:2,2] = np.nan
# 0 1 2
# 0 0.735103 NaN NaN
# 1 -0.659199 NaN NaN
# 2 -1.130695 NaN 0.084843
# 3 -0.278393 NaN 1.526452
# 4 -1.526626 -0.570291 0.650891
# 5 1.156203 -0.739917 -0.924147
# 6 0.082499 -0.343313 1.748674
# 删除缺失值
df.dropna(thresh=2) # 删除缺失值为2的数据
# 0 1 2
# 2 -1.130695 NaN 0.084843
# 3 -0.278393 NaN 1.526452
# 4 -1.526626 -0.570291 0.650891
# 5 1.156203 -0.739917 -0.924147
# 6 0.082499 -0.343313 1.748674
以上代码主要讲解了dropna中thresh参数的用法,指定thresh缺失值,只有某一行中出现此数量的缺失值,此行才会被删除
- 填充缺失值
df.fillna(0) # 将所有的nan替换为0
df.fillna(1:0.9,2:0) # 索引为1的列变为0.9,索引为2的列变为0
# 若在其中指定inplace=True可以直接修改原数据
# 0 1 2
# 0 0.735103 0.900000 0.000000
# 1 -0.659199 0.900000 0.000000
# 2 -1.130695 0.900000 0.084843
# 3 -0.278393 0.900000 1.526452
# 4 -1.526626 -0.570291 0.650891
# 5 1.156203 -0.739917 -0.924147
# 6 0.082499 -0.343313 1.748674
以上两种填充方法都是填充指定数据
df2.fillna(method='ffill')
# 0 1 2
# 0 0.699642 0.474279 1.306383
# 1 0.797090 0.294717 0.860378
# 2 0.974179 0.294717 -1.341360
# 3 -0.868484 0.294717 0.650945
# 4 -0.414434 0.294717 0.650945
# 5 1.678029 0.294717 0.650945
df2.fillna(method='ffill',limit=2)
# 0 1 2
# 0 0.699642 0.474279 1.306383
# 1 0.797090 0.294717 0.860378
# 2 0.974179 0.294717 -1.341360
# 3 -0.868484 0.294717 0.650945
# 4 -0.414434 NaN 0.650945
# 5 1.678029 NaN 0.650945
在fillna中指定method参数为ffill,可以自动填充nan为它上方的最近的不是nan的数据,若再设置limit参数,可以起到指定填充行数的效果
移除重复数据
- 检测重复数据
data.duplicated()
- 删除重复数据
data.drop_duplicates()
在出现重复数据的情况下,此种形式默认保留第一个出现的值
data.drop_duplicates(['k1']) # 根据某一列删除重复数据
在drop_duplicates方法中传入指定列,可以只根据某一列删除重复数据,只看这一指定列(或指定多列),若出现重复数据则进行删除,只保留第一个出现的值
data.drop_duplicates(keep='last')
在drop_duplicates方法中设置keep参数为last,在出现重复数据的情况下,可以保留最后一个出现的值
利用映射或函数转换数据
- 根据map传入的字典对每行或每列进行转换
# 创建dataframe
data = pd.DataFrame('food':['Apple','banana','orange','apple','Mango','tomato'],
'price':[4,3,3.5,6,12,3])
# food price
# 0 Apple 4.0
# 1 banana 3.0
# 2 orange 3.5
# 3 apple 6.0
# 4 Mango 12.0
# 5 tomato 3.0
meat = 'apple':'fruit',
'banana':'fruit',
'orange':'fruit',
'mango':'fruit',
'tomato':'vagetables'
data['class'] = data['food'].str.lower().map(meat)
# food price class
# 0 Apple 4.0 fruit
# 1 banana 3.0 fruit
# 2 orange 3.5 fruit
# 3 apple 6.0 fruit
# 4 Mango 12.0 fruit
# 5 tomato 3.0 vagetables
- 根据map传入的函数对每行或每列进行转换,能与上面的方法产生相同的效果
data['class1'] = data['food'].map(lambda x:meat[x.lower()])
# food price class class1
# 0 Apple 4.0 fruit fruit
# 1 banana 3.0 fruit fruit
# 2 orange 3.5 fruit fruit
# 3 apple 6.0 fruit fruit
# 4 Mango 12.0 fruit fruit
# 5 tomato 3.0 vagetables vagetables
替换值
# 创建Series
data = pd.Series([1,-999,2,-1000,3])
data.replace(-999,np.nan)
data.replace([-999,-1000],np.nan)
data.replace([-999,-1000],[np.nan,0])
以上代码分别列举了单值替换单值,单值替换多值,多值替换多值,同样的,使用字典也可以实现多值替换多值,代码如下
data.replace(-999:np.nan,-1000:0)
重命名轴索引
- 与重新索引相区分,重新索引只能使用以前的索引名(只是改变顺序),若使用新的索引名则全为nan
# 创建dataframe
data = pd.DataFrame(np.arange(12).reshape((3,4)),
index=['Beijing','Tokyo','New York'],
columns=['one','two','three','four'])
# one two three four
# Beijing 0 1 2 3
# Tokyo 4 5 6 7
# New York 8 9 10 11
# 重新索引
data.reindex(['Beijing','New York','Tokyo'])
# one two three four
# Beijing 0 1 2 3
# New York 8 9 10 11
# Tokyo 4 5 6 7
- 使用map方法进行轴索引的重命名,在原有数据上直接进行修改
# 大写操作
tran = lambda x:x[:4].upper()
data.index = data.index.map(tran)
print(data)
# one two three four
# BEIJ 0 1 2 3
# TOKY 4 5 6 7
# NEW 8 9 10 11
- 使用rename方法进行轴索引的重命名,返回新的对象
# rename
data.rename(index=str.title,columns=str.upper)
# ONE TWO THREE FOUR
# Beij 0 1 2 3
# Toky 4 5 6 7
# New 8 9 10 11
# 结合字典型对象对标签更新
data.rename(index='TOKY':'东京',columns='three':'第三年')
# one two 第三年 four
# BEIJ 0 1 2 3
# 东京 4 5 6 7
# NEW 8 9 10 11
离散化和面元划分
- 使用cut函数进行面元划分
age = [20,22,25,31,61,45,41,32]
bins = [18,25,35,60,100]
cats = pd.cut(age,bins)
cats
# [(18, 25], (18, 25], (18, 25], (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
# Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
cats.codes
# array([0, 0, 0, 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] 3
# (35, 60] 2
# (25, 35] 2
# (60, 100] 1
# dtype: int64
pd.cut(age,[18,26,36,61,100],right=False) # 指定右侧是开区间
# 面元名称
names = ['青年','年轻人','中年','老年']
pd.cut(age,bins,labels=names)
# ['青年', '青年', '青年', '年轻人', '老年', '中年', '中年', '年轻人']
# Categories (4, object): ['青年' < '年轻人' < '中年' < '老年']
data = np.random.rand(20)
pd.cut(data,4,precision=2) # precision指定小数位数
bins中是划分的面元,使用cut函数可以将数据与面元一一对应,将数据对应到相应面元中,默认区间左开右闭,可以设置right参数调整为左闭右开,同时面元的名称也可以通过labels参数进行设置
2. 使用qcut函数进行面元划分
# qcut函数 可以得到大小相等的面元
data = np.random.rand(1000)
cats = pd.qcut(data,4)
pd.value_counts(cats)
# (0.75, 0.999] 250
# (0.505, 0.75] 250
# (0.232, 0.505] 250
# (-0.000773, 0.232] 250
# dtype: int64
# 按比例进行面元划分
cats = pd.qcut(data,[0,0.1,0.5,0.9,1.])
pd.value_counts(cats)
# (0.505, 0.904] 400
# (0.102, 0.505] 400
# (0.904, 0.999] 100
# (-0.000773, 0.102] 100
# dtype: int64
与cut函数不同的是,qcut函数不直接指定划分的面元
检测和过滤异常值
- 通过调用abs方法获取绝对值,选出其中大于指定界限的数据
data[(np.abs(data)>3).any(1)]
# 选出包含绝对值大于3的数据的行
- 将超过指定界限的数据进行修改
data[np.abs(data)>3] = 3
# 将绝对值大于3的数据都修改为3
排列和随机采样
- 排列,首先利用permutation方法产生新顺序的数组,然后根据新顺序的数组对原数据进行重新排列
# 原数据
# 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
sam = np.random.permutation(5)
df.take(sam)
# 修改之后的数据
# 0 1 2 3
# 2 8 9 10 11
# 1 4 5 6 7
# 0 0 1 2 3
# 4 16 17 18 19
# 3 12 13 14 15
- 随机采样,sample函数可以随机选择指定行数的数据,另外若再设置replace参数为True,可以进行重复选择
df.sample(n=3)
# 0 1 2 3
# 2 8 9 10 11
# 3 12 13 14 15
# 4 16 17 18 19
ch = pd.Series([5,7,1,6,3])
ch.sample(n=10,replace=True)
# 3 6
# 2 1
# 4 3
# 1 7
# 1 7
# 2 1
# 3 6
# 1 7
# 4 3
# 1 7
# dtype: int64
字符串对象方法
- 分割字符串
val = 'a,b,c'
val.split(',')
- 去除分割字符串的空白符,包括\\n
p = [x.strip() for x in val.split(',')]
- 字符串加法
f,s,t = p
f+'::'+s+'::'+t
# 'a::b::c'
'::'.join(p)
# 'a::b::c'
- 检测字符串
'c' in p
# True
val.index(',')
# 1
val.find(':') # 没有查找到返回-1,找到则返回下标位置
# -1
- 替换字符串
val.replace(',','::')
# 'a::b::c'
正则表达式
- 拆分
import re
text = "foo bar\\t bat \\tqq"
re.split('\\s+',text)
# ['foo', 'bar', 'bat', 'qq']
res = re.compile('\\s+')
res.split(text)
# ['foo', 'bar', 'bat', 'qq']
reg = re.split(res,text)
# ['foo', 'bar', 'bat', 'qq']
- 模式匹配
(1)findall方法:返回字符串中所有的非重叠匹配模式,findall方法返回的是由所有模式组成的列表
res.findall(text)
# [' ', '\\t ', ' \\t']
(2)match方法:从字符串起始位置匹配模式,还可以对模式各部分进行分组,如果匹配到模式,则返回一个匹配项对象,否则返回None
t1 = re.match('f',text)
print(t1)
# <re.Match object; span=(0, 1), match='f'>
(3)search方法:扫描整个字符串以匹配模式,如果找到则返回一个匹配项对象,与match不同,其匹配项可以位于字符串的任意位置,而不仅仅是起始处
t2 = re.search('b',text)
print(t2)
# <re.Match object; span=(6, 7), match='b'>
pandas的矢量化
pandas字符串函数
- 使用contains方法来进行字符串的查找(字符串包含内容)
data.str.contains('gmail')
# a False
# b True
# c True
# d NaN
# dtype: object
- 使用split方法对字符串进行分割
data.str.split('@')
# a [dave, qq.com]
# b [steve, gmail.com]
# c [sam, gmail.com]
# d NaN
# dtype: object
- 使用findall方法来计算各字符串的模式列表
data.str.findall('@')
# a [@]
# b [@]
# c [@]
# d NaN
# dtype: object
- 使用切片方法对字符串进行截取
data.str[:5]
# a dave@
# b steve
# c sam@g
# d NaN
# dtype: object
总结
- 数据缺失,存在大量的空值:填充或者删除缺失数据
(1)检测缺失数据
(data.isnull()).sum()
(2)dropna
data.dropna(subset=['yuanjia'],inplace=True)
(3)fillna
- 数据存在重复值:删除重复值
(1)检测重复值
data.duplicated().sum()
(2)删除重复值
data.drop_duplicates(inplace=True)
- 数据类型不统一:数据类型转换
- 部分数据包含数值和字符串:字符串处理
data['shoujia'] = data.shoujia.map(lambda x:float(x.replace('万','')))
data['shoujia'] = data.shoujia.map(lambda x:x.replace('万','')).astype('float64')
- 部分数据存在异常值:删除异常值
data.drop([4,9],inplace=True)
- 少数数据不利于分析:数据替换
以上是关于Python数据分析的主要内容,如果未能解决你的问题,请参考以下文章