pandas 数据清洗
1. 去除 NaN 值
在Pandas的各类数据Series和DataFrame里字段值为NaN的为缺失数据,不代表0而是说没有赋值数据,类似于python中的None值。数据的缺失有很多原因,缺失不是错误、无效,需要对缺失的数据进行必要的技术处理,以便后续的计算、统计。
可以通过numpy 模块的 nan 得到NaN 值。
首先,可以通过isnull 和 notnull 方法查看有哪些NaN值,这两个方法返回的布尔值,指示该值是否是NaN值,结合sum 方法可以获取每列空值的数目以及总数。也可以通过 count 方法得到每列不为NaN值的数目。
dropna()
删除NaN 值 可以通过 dropna 方法,默认按行扫描(操作),会将每一行有NaN 值的那一行删除,同时默认是对原对象的副本操作,不会对原对象产生影响,也可以通过inplace 指示是否直接在原对象上操作。 示例:
代码语言:javascript复制import pandas as pd
import numpy as np
val = np.arange(10, 38).reshape(7, 4)
col = list("abcd")
idx = "cake make fake sake wake lake take".split()
df = pd.DataFrame(val, columns = col, index = idx)
df["e"] = np.nan
df.at["make", "e"] = 100
print(df)
print(df.dropna())
print(df)
"""
a b c d e print(df)
cake 10 11 12 13 NaN
make 14 15 16 17 100
fake 18 19 20 21 NaN
sake 22 23 24 25 NaN
wake 26 27 28 29 NaN
lake 30 31 32 33 NaN
take 34 35 36 37 NaN
a b c d e print(df.dropna())
make 14 15 16 17 100
a b c d e print(df)
cake 10 11 12 13 NaN
make 14 15 16 17 100
fake 18 19 20 21 NaN
sake 22 23 24 25 NaN
wake 26 27 28 29 NaN
lake 30 31 32 33 NaN
"""
dropna 方法可以选择删除
代码语言:javascript复制# 要删除一列或一行中全部都是nan 值的那一行或列,可以通过下面的方式
print("del cols is all NaNn", df.dropna(axis = 'columns', how='all')) # how='all' 指明要这一列全部是nan值才删除,axis 案列操作
print("del rows is all NaNn", df.dropna(axis = 'rows', how='all')) # axis 按行操作,how 原理同上
# 同时可以添加条件删除
print(df.dropna(axis = 1, thresh = 2)) # axis=1按列操作,thresh 指示这一列或行中有两个或以上的非NaN 值的行或列被保留
通过布尔判断,也是可以实现删除 NaN 的功能。
代码语言:javascript复制# 使用布尔逻辑判断,下面两句代码功能一致。
print(df.e[df.e.notnull()])
print(df.e.dropna())
2. 填充NaN 值
一般情况下直接将NaN删除或许并不是最好的选择因此可以通过将NaN值进行填充。
fillna()
fillna 方法可以将df 中的nan 值按需求填充成某值
代码语言:javascript复制# 将NaN值用0填充
df.fillna(0,inplace = True) # inplace 指明在原对象上直接修改
复杂的 使用向前 或 向后 填充数据,依旧使用fillna 方法,所谓向前 是指 取出现NaN值的前一列或前一行的数据来填充NaN值,向后同理
代码语言:javascript复制# 在df 的e 这一列上操作,默认下按行操作,向前填充数据,即取e列中最近的一个不为NaN值来填充接下去的NaN值
df["e"].fillna(method = 'ffill',inplace=True)
# 原理同上,只是取e列中最近的一个不为NaN值并且它的上一个数值是NaN值 的值来填充接下去的NaN值
df["e"].fillna(method = 'bfill',inplace=True)
# 对 gake 行操作,axis=0按行操作,取该行中最先出现的一个不为NaN值填充接下去的NaN值
df.loc["gake"].fillna(method = 'ffill',inplace=True, axis = 0)
# 原理同上,只是变成了向后取值
df.loc["gake"].fillna(method = 'bfill',inplace=True, axis = 0)
# 对整个df 正常,按列操作,取最先出现NaN值的前一列数值,用来填充接下去出现NaN值的全部列
df.fillna(method = 'ffill',inplace=True, axis = 1)
也可以通过重新赋值的赋值来填充NaN值,即将一个series 赋值给df 的某一列 来达到删除NaN值的目的。
interpolate()
利用插值函数interpolate()对列向的数据进行填值。实现插值填充数据,那么要求这列上必须得有一些数据才可以,至少2个,会对起点和终点间的NaN进行插值。 示例:
代码语言:javascript复制val = np.arange(10, 38).reshape(7, 4)
col = list("abcd")
idx = "cake make fake sake wake lake take".split()
df = pd.DataFrame(val, columns = col, index = idx)
df["e"] = np.nan
df.at["fake", "e"] = 100
df.at["lake", "e"] = 600
df["f"] = np.nan
df.loc["gake"] = np.nan
df.at["gake", "c"] = 700
df["e"].interpolate(inplace=True)
df["f"].interpolate(inplace=True)
"""
a b c d e f
cake 10 11 12 13 NaN NaN
make 14 15 16 17 NaN NaN
fake 18 19 20 21 100 NaN
sake 22 23 24 25 NaN NaN
wake 26 27 28 29 NaN NaN
lake 30 31 32 33 600 NaN
take 34 35 36 37 NaN NaN
gake NaN NaN 700 NaN NaN NaN df
a b c d e f
cake 10 11 12 13 NaN NaN
make 14 15 16 17 NaN NaN
fake 18 19 20 21 100.000000 NaN
sake 22 23 24 25 266.666667 NaN
wake 26 27 28 29 433.333333 NaN
lake 30 31 32 33 600.000000 NaN
take 34 35 36 37 600.000000 NaN
gake NaN NaN 700 NaN 600.000000 NaN df.interpolate()
"""
可以看出,当待填充的列或行符合条件时,会从最近的那个非NaN值开始将之后的位置全部填充,填充的数值为列上保留数据的最大值最小值之间的浮点数值。
3. 删除重复数据
对于数据源中的重复数据,一般来讲没有什么意义,所以一般情况下都会进行删除操作。
duplicated()
duplicated 方法可以返回重复数据的分布情况,以布尔值显示。
代码语言:javascript复制col = ["apple", "pearl", "watermelon"] * 4
pri = [2.50, 3.00, 2.75] * 4
df = pd.DataFrame({"fruit": col, "price" : pri})
df.duplicated()
# drop_duplicates 方法用于删除
df.drop_duplicates()
df.drop_duplicated(["page"], keep="first", inplace=True)
drop_duplicated()
删除重复值,可以指定inplace 是否在原对象上直接操作,keep= last first false 等 默认first保留第一次出现的重复数据,last同时保留最后一次出现的重复数据,false 不保留
使用如上。
4. 数据匹配替换
简单数据删除填充有时并不能满足需求,因此需要数据进行匹配替换满足更进一步的需求。
map()
map函数可以将某列数据映射成其它数据
代码语言:javascript复制oSeries = pd.Series(["a", "b", "c"], index = [2,3,1])
iSeries = pd.Series([100,200, 300], index = ["c","b","a"])
oSeries.map(iSeries)
"""
2 a
3 b
1 c
dtype: object oSeries
c 100
b 200
a 300
dtype: int64 iSeries
2 300
3 200
1 100
dtype: int64 oSeries.map(iSeries)
"""
# 将oSeries 的value 替换成了 iSeries 的value
map 一般对index 和 series 等使用。
replace()
将数据替换成其他数据,可以一对一的替换也可一堆多的替换数据。
代码语言:javascript复制# 对series 而言,简单的一对一替换
ss = pd.Series(["a", "b", "c"], index = [2,3,1])
ss.replace("b", "hello", inplace = True)
# 同样可以多对多替换
ss.replace(["c", "a"], ["hello", "world"], inplace = True)
# 字典形式传参也可以,key是待替换的值,value是替换的值
ss.replace({"c":"hello", "a" : "world"}, inplace = True)
# 对dataframe 而言,使用replace 稍有不同
idx = [1,3,2,4]
val = {'name' : "hello the cruel world".split(),
'growth' : [100, 125, 150, 200]}
df = pd.DataFrame(val, idx)
# 第一个以字典形式确定要替换被的元素,key为元素所在行,value为待替换数值,第二个参数是替换成的值
df.replace({"name" : "the"}, "THE", inplace = True)
5. apply() 方法使用
replace、dropna、fillna函数要么针对NaN的某行或某列或某个,这些函数的作用有限,本章介绍的apply等函数可以针对整个Series或DataFrame的各个值进行相应的数据的处理
对series 使用apply
代码语言:javascript复制# 对series 使用apply ,会将series 中的每个元素执行操作
s = pd.Series(np.arange(2,6))
s.apply(lambda x : 2 * x)
对dataframe 使用apply
代码语言:javascript复制# 对df 使用apply,都是按行或按列操作,不能保证对每一个元素进行操作
df = pd.DataFrame(val, index=idx, columns=col)
# 按行操作,对数据求和
print(type(df.apply(lambda col: col.sum(), axis='rows')))
# 按列操作,对数据求和
print(df.apply(lambda row: row.sum(), axis='columns'))
# df["hello x the"] = df.apply(lambda row: row.hello * row.the, axis=1)
df 中的applymap()
df 中使用applymap 可以对df 中的每一个元素进行操作
代码语言:javascript复制val = np.arange(10, 60).reshape(10, 5)
idx = np.arange(10, 20)
col = list('abcde')
df = pd.DataFrame(val, index = idx, columns = col)
# df 中的每一个元素都会被加3
print(df.applymap(lambda x : x 3))
pandas 数据拼接
1. concat() 拼接方法
对series 操作
代码语言:javascript复制s1 = pd.Series(np.arange(2,6))
s2 = pd.Series(np.arange(8,12))
# 后一个series 接在前一个series后面
ss = pd.concat([s1, s2])
对dataframe 操作
代码语言:javascript复制# 两个DataFrame的拼接 1). label和columns均相同的情况下:
col = "hello the cruel world".split()
idx = ["a", "b", "c", "d"]
val1 = np.arange(16).reshape(4, 4)
val2 = np.arange(20, 36).reshape(4, 4)
df1 = pd.DataFrame(val1, index = idx, columns = col)
df2 = pd.DataFrame(val2, index = idx, columns = col)
# 在列标 相同的情况下,就是后一个df 接在前一个df 后面
df12 = pd.concat([df1, df2])
当然,列标和行标不一定是对应的,这个时候两DataFrame未匹配上的label或columns下的值为NaN
concat 函数 同样的可以指定是按行操作还是按列操作。
指定拼接的轴,默认是列方向的拼接数据,可以指定concat 的形参axis为行上的拼接数据。
2. concat 的内外连接
concat 的内外连接,就是 join 参数的指定,为 inner 时为内连接,为outer 时外连接。
代码语言:javascript复制col1 = "hello the cruel world".split()
col2 = "hello the nice world".split()
idx1 = ["a", "b", "c", "d"]
idx2 = ["a", "b", "d", "e"]
val1 = np.arange(16).reshape(4, 4)
val2 = np.arange(20, 36).reshape(4, 4)
df1 = pd.DataFrame(val1, index = idx1, columns = col1)#
df2 = pd.DataFrame(val2, index = idx2, columns = col2)
# "***outer join"
pd.concat([df1, df2], join = "outer")
# "***inner join"
pd.concat([df1, df2], join = "inner")
# concat 的内外连接 实际上就是对两个df 求交集还是并集的选择
# 外连接就是并集,内连接就是交集
3. merge() 方法
merge函数可以真正实现数据库的内外连接,且外连接还可以有左右连接的特性。
补充:
内连接,对两张有关联的表进行内连接操作,结果表会是两张表的交集,例如A表和B表,如果是A 内连接(inner join)B表,结果表是以A为基准,在B中找寻A匹配的行,不匹配则舍弃,B内连接A同理。 外连接,分左外连接,右外连接,全连接,左外连接是左表上的所有行匹配右表,正常能匹配上的取B表的值,不能的取空值,右外连接同理,全连接则是取左并上右表的的所有行,没能匹配上的用空值填充。
merge 默认是内连接
代码语言:javascript复制# 默认情况下,merge函数是内连接
print(course.merge(choose))
# 变换两张表的位置,得到的结果并不一样
print(choose.merge(course))
merge可以进行左外连接,右外连接,全连接。
代码语言:javascript复制# 通过指定how 参数可以进行全连接,course表并上choose表得出结果
print(course.merge(choose, how = "outer"))
# 同理,choose表并上course表得出结果
print(choose.merge(course, how = "outer"))
# merge进行左右外连接
# course表左外连接choose表,结果保留course 的全部行及列,和choose表进行匹配,不匹配以空值替代
print course.merge(choose, how = "left")
# course 表右外连接choose表,结果保留choose表的全部行列,和course表进行匹配,同时course表的数据会显示在choose表前
print course.merge(choose, how = "right")
# choose表左外连接course,和course表右外连接choose表结果一致,但choose表的数据显示在前
print choose.merge(course, how = "left")
# 和course 左外连接 choose结果一样,但每列数据的排列会有区别,因为结果表会先显示左表的结果
print choose.merge(course, how = "right")
pandas 数据分组
1. groupby 方法
DataFrame数据对象经groupby()之后有ngroups和groups等属性,其本质是DataFrame类的子类DataFrameGroupBy的实例对象。ngroups反应的是分组的个数,而groups类似dict结构,key是分组的index或label,value则为index或label所对应的分组数据。size函数则是可以返回所有分组的字节大小。count函数可以统计分组后各列数据项个数。get_group函数可以返回指定组的数据信息。而discribe函数可以返回分组后的数据的统计数据。
简单的按单列分组
代码语言:javascript复制# 按单列进行分组
dg = df0.groupby("fruit")
# 打印查看按fruit分组后的每组组名,及详细信息
for n, g in dg:
print "group_name:", n, "n|",g,"|"
# 查看组名和 每组的数据信息
for n,_ in dg:
print "group_name:", n, "n|",dg.get_group(n),"|"
# 查看分组后的统计数据
print dg.describe()
也支持多列分组
代码语言:javascript复制dg1 = df0.groupby(["fruit", "supplier"])
for n, g in dg1:
print "multiGroup on:", n, "n|",g ,"|"
2. aggregate 聚合
在使用groupby 分组完成后,借助aggregate函数可以 经过分组后 每组进行操作。
agg的形参是一个函数会对分组后每列都应用这个函数。
代码语言:javascript复制# 分组后对每组数据求平均值
print dg1.agg(np.mean)
也可以应用多个函数
代码语言:javascript复制# 以列表的形式传入参数即可,会对每组都执行全部的聚合函数
print dg1.agg([np.mean, np.std, np.min, np.sum])
可以对每列数组进行不同的聚合操作
代码语言:javascript复制# 传入字典,key为列名,value为要执行的聚合函数
print dg1.agg({"price" : np.mean, "supplier" : np.max})
3. transform() 方法
可以作用于groupby之后的每个组的所有数据,之前的aggregate函数只能用于分组后组的每列数据。
代码语言:javascript复制import pandas as pd
import numpy as np
idx = [101,101,101,102,102,102,103,103,103]
idx = [101,102,103] * 3
name = ["apple","pearl","orange", "apple","pearl","orange","apple","pearl","orange"]
name = ["apple"] * 3 ["pearl"] * 3 ["orange"] * 3
price = [4.1,5.3,6.3,4.20,5.4,6.0,4.5,5.5,6.8]
price = [4] * 3 [5] * 3 [6] * 3
df0 = pd.DataFrame({ "fruit": name, "price" : price, "supplier" :idx})
dg1 = df0.groupby(["fruit"])
def f1(x):
return x 1
def f2(x):
return x 100
# 选择某一列,调用transform方法,对每个数据都执行f1函数
print dg1["price"].transform(f1)[:3]
print dg1["supplier"].transform(f2)[:3]
# 直接调用对每个元素都执行f2 函数
print dg1.transform(f2)[:3]
# [:3] 是只打印前三个元素的意思
pandas 时间序列
时间序列数据在金融、经济、神经科学、物理学里都是一种重要的结构化的数据表现形式。 pandas 最基本的时间序列类型就是以时间戳(TimeStamp)为 index 元素的 Series 类型。Python和Pandas里提供大量的内建工具、模块可以用来创建时间序列类型的数据。
1. datetime 模块
Python的datetime标准模块下的
date子类可以创建日期时间序列的数据
time子类可创建小时分时间数据
datetime子类则可以描述日期小时分数据
代码语言:javascript复制import datetime
# 日期小时分秒 日期数据
cur = datetime.datetime(2018,12,30, 15,30,59)
print(cur,type(cur))
# 获得日类类型的时间数据
d = datetime.date(2018,12,30)
print(d)
# 获得小时分数据
t = datetime.time(12, 30, 5)
print(t)
datetime的timedelta模块给出时间间隔(差)
借助timedelta 可以定义时间时间间隔
代码语言:javascript复制# 设置一个日期
cur0 = datetime.datetime(2018,12,30, 15,30,59)
# 获取 从 cur0 加上一天的时间间隔
cur1 = cur0 datetime.timedelta(days = 1)
# cur0 加上 10分钟的时间间隔
cur2 = cur0 datetime.timedelta(minutes = 10)
# cur0 加上 29分钟 1秒的时间间隔
cur3 = cur0 datetime.timedelta(minutes = 29,seconds = 1)
用datetime数据创建time series时间序列数据。意思就是用datetime创建的时间作为index。.
代码语言:javascript复制from datetime import datetime, timedelta
import numpy as np
import pandas as pd
b = datetime(2018,12,16, 17,30,55)
vi = np.random.randn(60)
ind = []
for x in range(60):
bi = b timedelta(minutes = x)
ind.append(bi)
# series 的index 为 一串时间数据
ts = pd.Series(vi, index = ind)
# 显示前5条数据
print ts[:5]