27. Pandas - 1

cover.png

由于文章太长造成了发布文章频繁崩溃,没办法拆分成2部分。请紧接着看

Hi, 大家好。我是茶桁。

先跟小伙伴们打个招呼,今天这节课呢,就是我们Python基础课程的最后一节课了。

在本节课之前,我们讲解了Python的基础,介绍了两个第三方库。而今天一样会介绍一个第三方库:Pandas。

虽然是最后一节课了,但是本节课的任务却是一点也不轻松。相比较而言,如果你以后从事的是数据治理和分析工作,那么本节课的内容可能会是你在今后工作中用到的最多的内容。我们需要学习行列索引的操作,数据的处理,数据的合并,多层索引,时间序列,数据的分组聚合(重点)。最后,我们会有一个案例的展示。

听着是不是很兴奋?那我们就开始吧。

在开始讲解pandas之前,我们讲解一些关于数据的小知识。

我们大部分人应该都用过Excel表格,而我们从数据库中拿到的数据,也基本上和Excel上的数据差不多,都是由行列组成的。可以直接导出为csv文件。

也就是说,我们大部分时候要处理的数据,基本上都一组二维数据。例如,我们今天最后案例要用到的一个电影数据(部分),如图:

move_dataset.png

这里面,我们就将数据通过行列来展示和定位。

了解了这一点之后,我们来开始学习pandas。

Pandas简介

image-20230822134436992.png

在之前的介绍中,我们发现很多的操作似乎都似曾相识,在NumPy中我们好像都接触过。

有这种感觉很正常,Pandas本身就是基于NumPy的一种工具,该⼯具是为了解决数据分析任务⽽创建的。Pandas 纳⼊了⼤量库和⼀些标准的数据模型,提供了⾼效地操作⼤型数据集所需的⼯具。pandas提供了⼤量能使我们快速 便捷地处理数据的函数和⽅法。

Series对象

Pandas基于两种数据类型:seriesdataframe

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对象,它的的三个参数dataindexcolumns 分别为数据、⾏索引和列索引。

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修改indexcolumns

一样,让我们先创建一组新的数据供我们使用:

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,原因是我们设置inplaceTrue,在当前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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容