由于文章太长造成了发布文章频繁崩溃,没办法拆分成2部分。请紧接着看
Hi, 大家好。我是茶桁。
先跟小伙伴们打个招呼,今天这节课呢,就是我们Python基础课程的最后一节课了。
在本节课之前,我们讲解了Python的基础,介绍了两个第三方库。而今天一样会介绍一个第三方库:Pandas。
虽然是最后一节课了,但是本节课的任务却是一点也不轻松。相比较而言,如果你以后从事的是数据治理和分析工作,那么本节课的内容可能会是你在今后工作中用到的最多的内容。我们需要学习行列索引的操作,数据的处理,数据的合并,多层索引,时间序列,数据的分组聚合(重点)。最后,我们会有一个案例的展示。
听着是不是很兴奋?那我们就开始吧。
在开始讲解pandas之前,我们讲解一些关于数据的小知识。
我们大部分人应该都用过Excel表格,而我们从数据库中拿到的数据,也基本上和Excel上的数据差不多,都是由行列组成的。可以直接导出为csv
文件。
也就是说,我们大部分时候要处理的数据,基本上都一组二维数据。例如,我们今天最后案例要用到的一个电影数据(部分),如图:
这里面,我们就将数据通过行列来展示和定位。
了解了这一点之后,我们来开始学习pandas。
Pandas简介
在之前的介绍中,我们发现很多的操作似乎都似曾相识,在NumPy中我们好像都接触过。
有这种感觉很正常,Pandas本身就是基于NumPy的一种工具,该⼯具是为了解决数据分析任务⽽创建的。Pandas 纳⼊了⼤量库和⼀些标准的数据模型,提供了⾼效地操作⼤型数据集所需的⼯具。pandas提供了⼤量能使我们快速 便捷地处理数据的函数和⽅法。
Series对象
Pandas基于两种数据类型:series
与dataframe
。
Series
是Pandas中最基本的对象,Series类似⼀种⼀维数组。事实上,Series基本上就是基于NumPy的数组对象来的。和 NumPy的数组不同,Series能为数据⾃定义标签,也就是索引(index),然后 通过索引来访问数组中的数据。
Dataframe
是⼀个⼆维的表结构。Pandas的dataframe可以存储许多种不同的数据类型,并且每⼀个坐标轴都有⾃⼰的标签。你可以把它想象成⼀个series的字典项。
在开始基于代码进行学习之前,我们需要应用一些必要项。
import numpy as np
import pandas as pd
from pandas import Series
from pandas import DataFrame as df
现在我们来看看Series的一些基本操作:
创建Series对象并省略索引
index
参数是可省略的,你可以选择不输⼊这个参数。如果不带 index
参数,Pandas 会⾃动⽤默认 index
进⾏索引,类似数组,索引值是 [0, ..., len(data) - 1]
sel = Series([1,2,3,4])
print(sel)
---
0 1
1 2
2 3
3 4
dtype: int64
我们之前在NumPy中学习了dtype, 以及它的相关数据类型。所以现在的dtype: int64
我们应该能理解是什么意思了。
我们看打印的结果,在1,2,3,4
前面,是Series默认生成的索引值。
通常我们会⾃⼰创建索引
sel = Series(data=[1,2,3,4], index= list('abcd'))
print(sel)
---
a 1
b 2
c 3
d 4
dtype: int64
这个时候,我们可以对这个Series对象操作分别获取内容和索引:
print(f'values: {sel.values}')
print(sel.index)
---
values: [1 2 3 4]
Index(['a', 'b', 'c', 'd'], dtype='object')
又或者,我们可以直接获取健值对(索引和值对)。
print(list(sel.iteritems()))
---
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
那么这种健值对的形式让你想到了什么?是字典对吧?
我们完全可以将字典转为Series:
dict={"red":100,"black":400,"green":300,"pink":900}
se3=Series(dict)
print(se3)
---
red 100
black 400
green 300
pink 900
dtype: int64
Series数据获取
在Series拿到数据转为Series对象之后,诺大的数据中,我们如何定位并获取到我们想要的内容呢?
Series在获取数据上,支持位置、标签、获取不连续数据,使用切片等方式。我们一个个的看一下:
Series对象同时⽀持位置和标签两种⽅式获取数据
print('索引下标',sel['c'])
print('位置下标',sel[2])
---
索引下标 3
位置下标 3
获取不连续的数据
print('位置切⽚\n',sel[1:3])# 左包含右不包含
print('\n索引切⽚\n',sel['b':'d'])# 左右都包含
---
位置切⽚
b 2
c 3
dtype: int64
索引切⽚
b 2
c 3
d 4
dtype: int64
我们看到结果,发现两组数据数量不对,但是其实我们获取的位置都是一样的。这是因为,位置切片的方式会是「左包含右不包含」的,而索引切片则是「左右都包含」。
重新赋值索引的值
除了获取数据之外,我们还可以对数据进行重新索引。其实在之前我们将索引的时候,第二种自己赋值的方式实际上就是一个重新赋值了,将自己定义的值替换了默认值。这里让我们再来看一下:
sel.index = list('dcba')
print(sel)
---
d 1
c 2
b 3
a 4
dtype: int64
还有一种重新索引的方法reindex
, 这会返回一个新的Series。调用reindex
将会重新排序,缺失值这会用NaN
填补。
print(sel.reindex(['b','a','c','d','e']))
---
b 3.0
a 4.0
c 2.0
d 1.0
e NaN
dtype: float64
在重新索引的时候,我们特意多增加了一个索引。在最后一位没有数据的情况下,缺失值被NaN
填补上了。
丢弃指定轴上的项
sel = pd.Series(range(10, 15))
print(sel)
print(sel.drop([2,3]))
---
0 10
1 11
2 12
3 13
4 14
dtype: int64
0 10
1 11
4 14
dtype: int64
使用drop
,会丢弃掉轴上的项,例子中,我们将2,3进行了丢弃。
Series进⾏算术运算操作
对 Series 的算术运算都是基于 index 进⾏的。我们可以⽤加减乘除(+ - * /
)这样的运算符对两个 Series 进⾏运算,Pandas 将会根据索引 index,对响应的数据进⾏计算,结果将会以浮点数的形式存储,以避免丢失精度。如果 Pandas 在两个 Series ⾥找不到相同的 index,对应的位置就返回⼀个空值 NaN
。
# Series 算数运算
series1 = pd.Series([1,2,3,4],['London','HongKong','Humbai','lagos'])
series2 = pd.Series([1,3,6,4],['London','Accra','lagos','Delhi'])
print('series1-series2:')
print(series1-series2)
print('\nseries1+series2:')
print(series1+series2)
print('\nseries1*series2:')
print(series1*series2)
---
series1-series2:
Accra NaN
Delhi NaN
HongKong NaN
Humbai NaN
London 0.0
lagos -2.0
dtype: float64
series1+series2:
Accra NaN
Delhi NaN
HongKong NaN
Humbai NaN
London 2.0
lagos 10.0
dtype: float64
series1*series2:
Accra NaN
Delhi NaN
HongKong NaN
Humbai NaN
London 1.0
lagos 24.0
dtype: float64
除此之外,Series的算术运算操作同样也支持NumPy的数组运算
sel = Series(data = [1,6,3,5], index = list('abcd'))
print(sel[sel>3]) # 布尔数组过滤
print(sel*2) # 标量乘法
print(np.square(sel)) # 可以直接加⼊到numpy的数学函数
---
b 6
d 5
dtype: int64
a 2
b 12
c 6
d 10
dtype: int64
a 1
b 36
c 9
d 25
dtype: int64
DataFrame
DataFrame(数据表)是⼀种2维数据结构,数据以表格的形式存储,分成若⼲⾏和列。通过 DataFrame,你能很⽅便地处理数据。常见的操作⽐如选取、替换⾏或列的数据,还能重组数据表、修改索引、多重筛选等。我们基本上可以把 DataFrame 理解成⼀组采⽤同样索引的 Series 的集合。调⽤ DataFrame()
可以将多种格式的数据转换为DataFrame对象,它的的三个参数data
、index
和columns
分别为数据、⾏索引和列索引。
DataFrame的创建
我们可以使用二维数组
df1 = df(np.random.randint(0,10,(4,4)),index=[1,2,3,4],columns=['a','b','c','d'])
print(df1)
---
a b c d
1 9 6 3 0
2 6 2 7 0
3 3 1 6 5
4 6 6 7 1
也可以使用字典创建
行索引由index决定,列索引由字典的键决定
dict={'Province': ['Guangdong', 'Beijing', 'Qinghai','Fujian'],'pop': [1.3, 2.5, 1.1, 0.7], 'year': [2022, 2022, 2022, 2022]}
df2= df(dict,index=[1,2,3,4])
print(df2)
---
Province pop year
1 Guangdong 1.3 2022
2 Beijing 2.5 2022
3 Qinghai 1.1 2022
4 Fujian 0.7 2022
使用from_dict
dict2={"a":[1,2,3],"b":[4,5,6]}
df6=df.from_dict(dict2)
print(df6)
---
a b
0 1 4
1 2 5
2 3 6
索引相同的情况下,相同索引的值会相对应,缺少的值会添加NaN
data = {
'Name':pd.Series(['zs','ls','we'],index=['a','b','c']),
'Age':pd.Series(['10','20','30','40'],index=['a','b','c','d']),
'country':pd.Series(['中国','⽇本','韩国'],index=['a','c','b'])
}
df = pd.DataFrame(data)
print(df)
---
Name Age country
a zs 10 中国
b ls 20 韩国
c we 30 ⽇本
d NaN 40 NaN
看了那么多DataFrame的转换方式,那我们如何将数据转为字典呢?DataFrame有一个内置方法to_dict()
能将DataFrame对象转换为字典:
dict = df.to_dict()
print(dict)
---
{'Name': {'a': 'zs', 'b': 'ls', 'c': 'we', 'd': nan}, 'Age': {'a': '10', 'b': '20', 'c': '30', 'd': '40'}, 'country': {'a': '中国', 'b': '韩国', 'c': '⽇本', 'd': nan}}
DataFrame对象常⽤属性
让我们先来生成一组数据备用:
df_dict = {
'name':['James','Curry','Iversion'],
'age':['18','20','19'],
'national':['us','China','us'] }
df = pd.DataFrame(data=df_dict,index=['0','1','2'])
print(df)
---
name age national
0 James 18 us
1 Curry 20 China
2 Iversion 19 us
获取⾏数和列数
print(df.shape)
---
(3,3)
获取⾏索引
print(df.index.tolist())
---
['0', '1', '2']
获取列索引
print(df.columns.tolist())
---
['name', 'age', 'national']
获取数据的类型
print(df.dtypes)
---
name object
age object
national object
dtype: object
获取数据的维度
print(df.ndim)
---
2
values属性也会以⼆维ndarray的形式返回DataFrame的数据
print(df.values)
---
[['James' '18' 'us']
['Curry' '20' 'China']
['Iversion' '19' 'us']]
展示df的概览
print(df.info())
---
<class 'pandas.core.frame.DataFrame'>
Index: 3 entries, 0 to 2
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 name 3 non-null object
1 age 3 non-null object
2 national 3 non-null object
dtypes: object(3)
memory usage: 96.0+ bytes
None
显示头⼏⾏,默认显示5⾏
print(df.head(2))
---
name age national
0 James 18 us
1 Curry 20 China
显示后⼏⾏
print(df.tail(1))
---
name age national
2 Iversion 19 us
获取DataFrame的列
print(df['name'])
---
0 James
1 Curry
2 Iversion
Name: name, dtype: object
因为我们只获取⼀列,所以返回的就是⼀个Series
print(type(df['name']))
---
<class 'pandas.core.series.Series'>
如果获取多个列,那返回的就是⼀个DataFrame 类型:
print(df[['name','age']])
print(type(df[['name','age']]))
---
name age
0 James 18
1 Curry 20
2 Iversion 19
<class 'pandas.core.frame.DataFrame'>
获取一行
print(df[0:1])
---
name age national
0 James 18 us
去多⾏
print(df[1:3])
---
name age national
1 Curry 20 China
2 Iversion 19 us
取多⾏⾥⾯的某⼀列(不能进⾏多⾏多列的选择)
print(df[1:3][['name','age']])
---
name age
1 Curry 20
2 Iversion 19
⚠️ 注意:df[]
只能进⾏⾏选择,或列选择,不能同时多⾏多列选择。比如在NumPy中的data[:,1:3]
这种是不行的。当然,并不是没有办法获取,我们接着往下看:
df.loc
通过标签索引⾏数据;df.iloc
通过位置获取⾏数据
获取某⼀⾏某⼀列的数据
print(df.loc['0','name'])
---
James
⼀⾏所有列
print(df.loc['0',:])
---
name James
age 18
national us
Name: 0, dtype: object
某⼀⾏多列的数据
print(df.loc['0',['name','age']])
---
name James
age 18
Name: 0, dtype: object
选择间隔的多⾏多列
print(df.loc[['0','2'],['name','national']])
---
name national
0 James us
2 Iversion us
选择连续的多⾏和间隔的多列
print(df.loc['0':'2',['name','national']])
---
name national
0 James us
1 Curry China
2 Iversion us
取⼀⾏
print(df.iloc[1])
---
name Curry
age 20
national China
Name: 1, dtype: object
取连续多⾏
print(df.iloc[0:2])
---
name age national
0 James 18 us
1 Curry 20 China
取间断的多⾏
print(df.iloc[[0,2],:])
---
name age national
0 James 18 us
2 Iversion 19 us
取某⼀列
print(df.iloc[:,1])
---
0 18
1 20
2 19
Name: age, dtype: object
某⼀个值
print(df.iloc[1,0])
---
Curry
修改值
df.iloc[0,0]='panda'
print(df)
---
name age national
0 panda 18 us
1 Curry 20 China
2 Iversion 19 us
dataframe中的排序⽅法
df = df.sort_values(by='age', ascending*=False)
print(df)
---
name age national
1 Curry 20 China
2 Iversion 19 us
0 panda 18 us
ascending=False
是降序排列,默认为True
,也就是升序。
dataframe修改index、columns
一样,让我们先创建一组新的数据供我们使用:
df1 = pd.DataFrame(np.arange(9).reshape(3, 3), index = ['bj', 'sh', 'gz'], columns=['a', 'b', 'c'])
print(df1)
---
a b c
bj 0 1 2
sh 3 4 5
gz 6 7 8
修改 df1 的 index
df1.index
可以打印出df1的索引值,同时也可以为其赋值。
print(df1.index) # 可以打印出print的值,同时也可以为其赋值
df1.index = ['beijing', 'shanghai', 'guangzhou']
print(df1)
---
Index(['bj', 'sh', 'gz'], dtype='object')
a b c
beijing 0 1 2
shanghai 3 4 5
guangzhou 6 7 8
⾃定义map函数(x是原有的⾏列值)
# ⾃定义map函数(x是原有的⾏列值)
def test_map(x):
return x+'_ABC'
print(df1.rename(index=test_map, columns=test_map, inplace=True))
print(df1)
---
None
a_ABC b_ABC c_ABC
beijing_ABC 0 1 2
shanghai_ABC 3 4 5
guangzhou_ABC 6 7 8
其中的inplace
传入一个布尔值,默认为False。指定是否返回新的DataFrame。如果为True,则在原df上修改, 返回值为None。
rename
可以传⼊字典,为某个 index 单独修改名称
df3 = df1.rename(index={'beijing_ABC':'beijing'}, columns = {'a_ABC':'aa'})
print(df3)
---
aa b_ABC c_ABC
beijing 0 1 2
shanghai_ABC 3 4 5
guangzhou_ABC 6 7 8
列转化为索引
df1=pd.DataFrame({'X':range(5),'Y':range(5),'S':list("abcde"),'Z': [1,1,2,2,2]})
print(df1)
---
X Y S Z
0 0 0 a 1
1 1 1 b 1
2 2 2 c 2
3 3 3 d 2
4 4 4 e 2
指定⼀列为索引 (drop=False
指定同时保留作为索引的列)
result = df1.set_index('S',drop=False)
result.index.name=None
print(result)
---
X Y S Z
a 0 0 a 1
b 1 1 b 1
c 2 2 c 2
d 3 3 d 2
e 4 4 e 2
⾏转为列索引
result = df1.set_axis(df1.iloc[0],axis=1,inplace=False)
result.columns.name=None
print(result)
---
0 0 a 1
0 0 0 a 1
1 1 1 b 1
2 2 2 c 2
3 3 3 d 2
4 4 4 e 2
添加数据
先增加一组数据:
df1 = pd.DataFrame([['Snow','M',22],['Tyrion','M',32],['Sansa','F',18], ['Arya','F',14]],columns=['name','gender','age'])
print(df1)
---
name gender age
0 Snow M 22
1 Tyrion M 32
2 Sansa F 18
3 Arya F 14
在数据框最后加上score⼀列,注意增加列的元素个数要跟原数据列的个数⼀样。
df1['score']=[80,98,67,90]
print(df1)
---
name gender age score
0 Snow M 22 80
1 Tyrion M 32 98
2 Sansa F 18 67
3 Arya F 14 90
在具体某个位置插⼊⼀列可以⽤insert
的⽅法, 语法格式:列表.insert(index, obj)
index
--->对象 obj 需要插⼊的索引位置。
obj
---> 要插⼊列表中的对象(列名)
将数据框的列名全部提取出来存放在列表⾥
col_name=df1.columns.tolist()
print(col_name)
---
['name', 'gender', 'age', 'score']
在列索引为2的位置插⼊⼀列,列名为:city
col_name.insert(2,'city')
print(col_name)
---
['name', 'gender', 'city', 'age', 'score']
刚插⼊时不会有值,整列都是NaN,我们使用DataFrame.reindex()
对原⾏/列索引重新构建索引值
df1=df1.reindex(columns=col_name)
print(df1)
---
name gender city age score
0 Snow M NaN 22 80
1 Tyrion M NaN 32 98
2 Sansa F NaN 18 67
3 Arya F NaN 14 90
给city列赋值
df1['city']=['北京京','⼭⻄西','湖北北','澳⻔门']
print(df1)
---
name gender city age score
0 Snow M 北京京 22 80
1 Tyrion M ⼭⻄西 32 98
2 Sansa F 湖北北 18 67
3 Arya F 澳⻔门 14 90
df中的insert
是插⼊⼀列。语法和关键参数为:
df.insert(iloc,column,value)
iloc
:要插⼊的位置
colunm
:列名
value
:值
刚才我们插入city
列的时候省略了value
,所以新建列值全部为NaN
,这次我们加上再看:
df1.insert(2,'score2',[80,98,67,90])
print(df1)
---
name gender score2 city age score
0 Snow M 80 北京京 22 80
1 Tyrion M 98 ⼭⻄西 32 98
2 Sansa F 67 湖北北 18 67
3 Arya F 90 澳⻔门 14 90
插⼊⼀⾏
# 插⼊⼀⾏
row=['111','222','333','444','555','666']
df1.iloc[1]=row
print(df1)
---
name gender score2 city age score
0 Snow M 80 北京京 22 80
1 111 222 333 444 555 666
2 Sansa F 67 湖北北 18 67
3 Arya F 90 澳⻔门 14 90
插入行的时候,列个数必须对应才可以,否则会报错。
目前这组数据已经被我们玩乱了,我们再重新生成一组数据来看后面的:
df1 = pd.DataFrame([['Snow','M',22],['Tyrion','M',32],['Sansa','F',18],['Arya','F',14]],columns=['name','gender','age'])
print(df1)
---
name gender age
0 Snow M 22
1 Tyrion M 32
2 Sansa F 18
3 Arya F 14
再继续创建一组数据,我们将尝试将两组数据进行合并, 新创建的这组数据⽤来增加进数据框的最后⼀⾏。
new=pd.DataFrame({'name':'lisa','gender':'F','age':19},index=[0])
print(new)
---
name gender age
0 lisa F 19
在原数据框df1最后⼀⾏新增⼀⾏,⽤append
⽅法
df1=df1.append(new,ignore_index=True)
print(df1)
---
name gender age
0 Snow M 22
1 Tyrion M 32
2 Sansa F 18
3 Arya F 14
4 lisa F 19
ignore_index=False
,表示不按原来的索引, 从0开始⾃动递增。
objs
:合并对象
axis
:合并⽅式,默认0表示按列合并,1表示按⾏合并
ignore_index
:是否忽略索引
df1 = pd.DataFrame(np.arange(6).reshape(3,2),columns=['four','five'])
df2 = pd.DataFrame(np.arange(6).reshape(2,3),columns=['one','two','three'])
print(df1)
print(df2)
---
four five
0 0 1
1 2 3
2 4 5
one two three
0 0 1 2
1 3 4 5
按行合并
result = pd.concat([df1,df2],axis=1)
print(result)
---
four five one two three
0 0 1 0.0 1.0 2.0
1 2 3 3.0 4.0 5.0
2 4 5 NaN NaN NaN
按列合并
four five one two three
0 0.0 1.0 NaN NaN NaN
1 2.0 3.0 NaN NaN NaN
2 4.0 5.0 NaN NaN NaN
3 NaN NaN 0.0 1.0 2.0
4 NaN NaN 3.0 4.0 5.0
看结果我们能看出来,在合并的时候,如果对应不到值,那么就会默认添加NaN
值。
DataFrame的删除
df2 = pd.DataFrame(np.arange(9).reshape(3,3),columns=['one','two','three'])
print(df2)
df3=df2.drop(['one'],axis=1, inplace=True)
print(df2)
print(df3)
---
one two three
0 0 1 2
1 3 4 5
2 6 7 8
two three
0 1 2
1 4 5
2 7 8
None
lables
:要删除数据的标签
axis
:0表示删除⾏,1表示删除列,默认0
inplace
:是否在当前df中执⾏此操作
最后的返回值为None
,原因是我们设置inplace
为True
,在当前df
中执行操作。如果我们将其设置为False
,则会将操作后的值进行返回,生成一个新的对象。
df3=df2.drop([0,1],axis=0, inplace=False)
print(df2)
print(df3)
---
one two three
0 0 1 2
1 3 4 5
2 6 7 8
one two three
2 6 7 8
数据处理
在我们查看完DataFrame的基础操作之后,我们现在来正式开始数据处理。
我们可以通过通过dropna()
滤除缺失数据,先让我们创建一组数据:
se=pd.Series([4,NaN,8,NaN,5])
print(se)
---
0 4.0
1 NaN
2 8.0
3 NaN
4 5.0
dtype: float64
尝试清除缺失数据,也就是NaN
值:
print(se.dropna())
---
0 4.0
2 8.0
4 5.0
dtype: float64
在清除数据之前,我们有两个方法可以判断当前数据中是否有缺失数据,不过这两个方法的判断方式是相反的,一个是判断不是缺失数据,一个判断是缺失数据:
print(se.notnull())
print(se.isnull())
---
0 True
1 False
2 True
3 False
4 True
dtype: bool
0 False
1 True
2 False
3 True
4 False
dtype: bool
那既然有方法可以进行判断当前数据是否为缺失数据,那么我们用之前的方法与其配合,一样可以做滤除操作:
print(se[se.notnull()])
---
0 4.0
2 8.0
4 5.0
dtype: float64
当然,除了Series对象之外,我们还需要进行处理DataFrame对象
df1=pd.DataFrame([[1,2,3],[NaN,NaN,2],[NaN,NaN,NaN],[8,8,NaN]])
print(df1)
---
0 1 2
0 1.0 2.0 3.0
1 NaN NaN 2.0
2 NaN NaN NaN
3 8.0 8.0 NaN
默认滤除所有包含NaN
:
print(df1.dropna())
---
0 1 2
0 1.0 2.0 3.0
传⼊how=‘all’
滤除全为NaN
的⾏:
print(df1.dropna(how='all'))
---
0 1 2
0 1.0 2.0 3.0
1 NaN NaN 2.0
3 8.0 8.0 NaN
可以看到,除了下标为2
的那一行之外,其余含NaN
值的行都被保留了。
之前操作最后只留下一行,原因是how
的默认值为how='any'
。只要是nan
就删除
传⼊axis=1滤除列:
print(df1.dropna(axis=1,how="all"))
---
0 1 2
0 1.0 2.0 3.0
1 NaN NaN 2.0
2 NaN NaN NaN
3 8.0 8.0 NaN
为什么没有变化?按列来查看,没有一列是全是NaN
值的。
除了how
值外,我们还可以可以使用thresh
来精准操作,它可以传入一个数值n
,会保留至少n
个非NaN
数据的行或列:
print(df1.dropna(thresh=2))
---
0 1 2
0 1.0 2.0 3.0
3 8.0 8.0 NaN
仅有一个非NaN
的行和全部为NaN
的行就都被滤除了。
那NaN
是不是就只能被删除了呢?并不是,还记得我们之前操作的时候我提到过,我们大多数遇到NaN
值的时候,基本都是用平均值来进行填充,这是一个惯例操作。
那么,我们来看看如何填充缺失数据
df1=pd.DataFrame([[1,2,3],[NaN,NaN,2],[NaN,NaN,NaN],[8,8,NaN]])
print(df1)
---
0 1 2
0 1.0 2.0 3.0
1 NaN NaN 2.0
2 NaN NaN NaN
3 8.0 8.0 NaN
⽤常数填充fillna
print(df1.fillna(0))
---
0 1 2
0 1.0 2.0 3.0
1 0.0 0.0 2.0
2 0.0 0.0 0.0
3 8.0 8.0 0.0
传⼊inplace=True
直接修改原对象:
df1.fillna(0,inplace=True)
print(df1)
---
0 1 2
0 1.0 2.0 3.0
1 0.0 0.0 2.0
2 0.0 0.0 0.0
3 8.0 8.0 0.0
通过字典填充不同的常数
print(df1.fillna({0:10,1:20,2:30}))
---
0 1 2
0 1.0 2.0 3.0
1 10.0 20.0 2.0
2 10.0 20.0 30.0
3 8.0 8.0 30.0
还有我们之前提到过的,填充平均值:
print(df1.fillna(df1.mean()))
---
0 1 2
0 1.0 2.0 3.0
1 4.5 5.0 2.0
2 4.5 5.0 2.5
3 8.0 8.0 2.5
当然,我们可以只填充某一列
print(df1.iloc[:,1].fillna(5,inplace = True))
print(df1)
---
None
0 1 2
0 1.0 2.0 3.0
1 NaN 5.0 2.0
2 NaN 5.0 NaN
3 8.0 8.0 NaN
传⼊method=” “
会改变插值⽅式,先来一组数据,并在其中加上NaN
值
df2=pd.DataFrame(np.random.randint(0,10,(5,5)))
df2.iloc[1:4,3]=NaN
df2.iloc[2:4,4]=NaN
print(df2)
---
0 1 2 3 4
0 3 5 9 9.0 3.0
1 0 1 2 NaN 8.0
2 6 5 8 NaN NaN
3 5 6 5 NaN NaN
4 5 3 5 8.0 2.0
现在,我们用前面的值来填充, method ='ffill'
print(df2.fillna(method='ffill'))
---
0 1 2 3 4
0 3 5 9 9.0 3.0
1 0 1 2 9.0 8.0
2 6 5 8 9.0 8.0
3 5 6 5 9.0 8.0
4 5 3 5 8.0 2.0
用后面的值来填充method='bfill'
:
print(df2.fillna(method='bfill',limit=1))
---
0 1 2 3 4
0 7 1 8 0.0 0.0
1 6 8 4 NaN 5.0
2 6 2 5 NaN NaN
3 4 8 0 1.0 3.0
4 8 0 2 1.0 3.0
以上代码中,我们还传入了limit
, 用于限制填充行数。
当我们传入axis
的时候,会修改填充方向:
print(df2.fillna(method="ffill",limit=1,axis=1))
---
0 1 2 3 4
0 0.0 8.0 9.0 4.0 7.0
1 2.0 8.0 0.0 0.0 9.0
2 1.0 8.0 5.0 5.0 NaN
3 0.0 0.0 3.0 3.0 NaN
4 2.0 9.0 4.0 6.0 3.0
接着,我们再来看看如何移除重复数据,俗称「去重」:
DataFrame中经常会出现重复⾏,利⽤duplicated()
函数返回每⼀⾏判断是否重复的结果(重复则为 True)
df1=pd.DataFrame({'A':[1,1,1,2,2,3,1],'B':list("aabbbca")})
print(df1)
print(df1.duplicated())
---
A B
0 1 a
1 1 a
2 1 b
3 2 b
4 2 b
5 3 c
6 1 a
0 False
1 True
2 False
3 False
4 True
5 False
6 True
dtype: bool
去除全部的重复⾏
print(df1.drop_duplicates())
---
A B
0 1 a
2 1 b
3 2 b
5 3 c
指定列去除重复行
print(df1.drop_duplicates(['A']))
---
A B
0 1 a
3 2 b
5 3 c
保留重复⾏中的最后⼀⾏
df1=pd.DataFrame({'A':[1,1,1,2,2,3,1],'B':list("aabbbca")})
print(df1.drop_duplicates(['A'],keep='last'))
---
A B
4 2 b
5 3 c
6 1 a
去除重复的同时改变DataFrame对象
df1.drop_duplicates(['A','B'],inplace=True)
print(df1)
---
A B
0 1 a
2 1 b
3 2 b
5 3 c