上次的内容给大家介绍了文件的存储和读取,还自己实现了重复数据的删除。这节课我们将要从以上几个方面继续探索对表格数据的处理。
空值的删除
空值揭秘
在一个表格文件中,经常会出现存在空白的位置: 我们先看看一张含有空值的表格读取出来是什么样子。首先,将文件保存到默认路径下,然后进行读取:
import pandas as pd
from pandas import DataFrame,Series
df=pd.read_excel('./rate.xlsx')
print(df)
print(df.shape)
先看看结果:
通过结果对比不难发现,空值在读取时会被记录为NaN。然而这里却有一个问题,如果我们想要查看完整的列表时,直接这样写代码可能是看不到的,因为当数据量过多时数据就会被计算机无情省略。对于这一问题,我们在打印数据之前加上这段代码就可以了:
pd.set_option('display.max_columns',None)
pd.set_option('display.max_rows',None)
pd.set_option('max_colwidth',100)
pd.set_option('display.width',1000)
这四句代码不是必须都加的,大家可以自行运行查看结果,选择自己喜欢的输出方式设置。 说回到空值的问题,现在我们查看一下NaN的数据类型:
print(type(df.loc[3,'Change 1990-2015']))
可以看到,NaN实际上是float类型,我们可以从numpy库里调用nan,并且有nan参与的运算,结果都是nan:
from numpy import nan as NaN
print(NaN+1)
notnull方法
我们先创建一个较小的数据集进行学习。
df=DataFrame([[0,0,0,0,0,0,NaN,0],[1,1,1,1,1,1,NaN,1],[2,2,2,2,2,2,NaN,2],\
[3,3,3,3,3,3,NaN,3],[NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN],[4,4,4,4,4,4,NaN,4]])
df.to_csv(path_or_buf='./test_data', encoding='utf_8_sig')
notnull方法可以删除Series中的空值,我们取出刚刚存储的csv文件第一行作为演示:
from pandas import DataFrame,Series
from numpy import nan as NaN
import pandas as pd
df=pd.read_csv('./test_data.csv')
se=Series(list(df.iloc[0]))
print(se.notnull())
print(se[se.notnull()])
运行结果: 通过注释大家也可以发现,我们这种操作并没有真正意义上删除了空值,只是没有打印出来。我们可以用se.notnull()为True的值重新建立一个表格:
df=pd.read_csv('test_data.csv',index_col=0)
new_form=[]
long=df.shape[0]
for i in range(long):
se=Series(list(df.iloc[i]))
if not se[se.notnull()].empty:
new_form.append(se[se.notnull()])
print(DataFrame(data=new_form))
运行之后,可以打开.csv文件与终端进行对比: 是不是原本的数据并没有改变呢?博主做的数据很简单,没有添加索引,所以重新构建DataFrame类也不难,但是如果遇到复杂的表格,这样的处理方法就十分复杂,所以我们要学习另一种方法:
dropna方法
dropna是一个可以删除DataFrame类数据中含有的NaN数据的方法,先看一下例子:
df=pd.read_csv('test_data.csv',index_col=0)
df=df.dropna()
print(df)
看一下运行结果: 终端得到了一个空的DataFrame,而.csv文件也没有发生变化,说明我们针对数据的改动同样不影响原数据集df。而终端为空是因为dropna()会删除所有的含有NaN的行。如果我们需要删除整行/列为NaN的值,需要添加how='all’参数,并用axis制定行列:
df=pd.read_csv('test_data.csv',index_col=0)
df1=df.dropna(how='all')
print(df1)
df2=df.dropna(how='all',axis=1)
print(df2)
df3=df.dropna(labels=[0,1],axis=0)
print(df3)
df3=df.dropna(labels=[0,1],axis=1)
print(df3)
看看运行结果: 都没有改变df原本的值。这种方法虽然比刚才方便了很多,但依然有不小的限制,相信大家会想,如果我们能够自己指定删除内容,并且能够保存结果应该会更好。
drop方法
如果我们只是想删除单独的数据,亦或是保存修改后的数据,drop是有办法做到的。完整的drop方法含有的参数如下:
DataFrame.drop(labels=None,axis=0, index=None, columns=None, inplace=False)
labels :要删除行/列索引名,用列表给定;配合后面的axis指明要删除的是行还是列。 index: 直接指定要删除的行。 columns: 直接指定要删除的列。 inplace=False:Flase为该参数的默认值。此删除操作不改变原数据,而是返回一个执行删除操作后的新dataframe。 inplace=True:直接在原数据上进行删除操作,删除后无法返回。
下面我们一一举例:
df=pd.read_csv('test_data.csv',index_col=0)
df1=df.drop(labels=[0,1],axis=0)
print(df1)
df2=df.drop(labels=['0','1'],axis=1)
print(df2)
df3=df.drop(columns='1')
print(df3)
注意,当我们没有定义标签索引时,位置行索引是数字类型,而列索引则是字符类型。看看结果: 下面我们尝试保存修改的内容(修改之后再想使用原数据需要重新生成):
df=pd.read_csv('test_data.csv',index_col=0)
df1=df.drop(labels=[0,1],axis=0,inplace=True)
print(df1)
print(df)
虽然我们是在原数据集(df)上改掉了数据,但是这也并不意味着会影响.csv文件内容哦。 以上就是删除操作的内容了,下面我们继续学习对数据集的其他处理方法。
空值的填补
对空值的处理方法其实很少,因为一个数据集,列与列之间的数据类型很多情况下是不一样的,所以能填充的方法只有用常数填充,利用同一列的数据对空值进行填充或者使用类型相同的列之间的运行结果对空值进行填充。接下来我们会举例说明
fillna方法
首先我们看一下该方法所用到的参数:
df.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
这个方法可以完成用常数填充,用上一行同一列数据进行填充,用一列的平均值进行填充,需要注意的是method和value不能同时出现。下面我们用具体例子来告诉大家这个方法该如何使用:
df=pd.read_csv('test_data.csv',index_col=0)
print('用0填充:')
print(df.fillna(0))
print('用一列平均值填充:')
print(df.fillna(df.mean()))
print('用同一列前一行的值填充:')
df.fillna(method='ffill',axis=0,inplace=True)
print(df)
列间运算填充
我们先构建一个DataFrame类:
from pandas import DataFrame,Series
from numpy import nan as NaN
import pandas as pd
df=DataFrame(data=[[1,'Product_001',9.82,5,NaN],
[2,'Product_002',11.99,4,NaN],
[3,'Product_003',9.62,6,NaN],
[4,'Product_004',11.08,8,NaN],
[5,'Product_005',7.75,3,NaN]],columns=['ID','Name','OnePrice','Count','Price'])
其中,价格等于单价和数量的乘积。因为单价、数量和价格都是完全按行对应的,因此我们可以直接用以下方法求值:
df['Price']=df['OnePrice']*df['Count']
print(df)
看看结果: 当然,实际使用之中可能出现前几行或者中间几行的内容不符合相乘运算的规则,我们可以使用以下的方式进行处理,跳过不能相乘的行,或者将不在一行的数值相乘并复制到另一位置:
end=df.shape[0]
for i in range(2,end):
df['Price'].iloc[i]=df['Count'].iloc[i]*df['OnePrice'].iloc[i]
print(df)
博主第一次写这段代码时是这样写的:
print(df.loc[1:df.shape[0],'OnePrice':'Price'])
for val in df.loc[1:df.shape[0],'OnePrice':'Price']:
val['Price']=val['Count']*val['OnePrice']
print(df)
这段代码是无法正常运行的,原因是val只会得到列标签索引的值,而不会得到每一行的数据。我们可以记住一个套路:要先确定想操作的列,再去找(不同)行。之后在写代码的时候希望大家注意。
重复数据的处理
大家还有印象上一篇博文中我们是如何处理重复的数据吗?实际上pandas里也有处理重复数据的方法哦,调用起来非常简单。
duplicated方法
重复数据的存在有时不仅会降低分析的准确度,也会降低分析的效率。所以我们在整理数据的时候应该将重复的数据删除掉。duplicated()方法可以判断DataFrame类每一行数据是否有重复,如果前面出现过,返回False,否则返回True,我们还用张三和他的小伙伴们的那组数据举例:
from pandas import DataFrame
import pandas as pd
dic = {'ID':[1001,1002,1003,1001,1004,1005,1006,1004,1007,1008],
'name':['张三','李四','王五','张三','赵六','孙七','周八','赵六','吴九','郑十'],
'age':['18','19','30','18','20','22','25','20','21','16'],
'high':['150.00','167.00','180.00','150.00','160.00','165.00','168.00','160.00','172.00','178.00'],
'sex':['男','女','男','男','女','男','男','女','女','男']}
df=pd.DataFrame(data = dic,
index = ['a','b','c','d','e','f','g','h','i','j'])
print(df.duplicated())
看看结果: 看,重复的数据已经被我们的duplicated方法标注出来了。
drop_duplicates方法
实际上,大部分情况下光是知道那个数据重复了还是不够的,我们为了正确高效的处理数据,通常会想办法将其删掉,这就需要drop_duplicates()方法了:
print(df.drop_duplicates())
结果为: 当然,这个方法是默认不修改原数据集的,也就是df还是原来的df。 如果需要的话,也可以删除某一列的重复值,并覆盖掉之前的数据集:
df.drop_duplicates(['sex'],inplace=True)
print(df)
结果为:
小结练习
到这里,我们已经学过了空值的处理,空值的填补以及重复数据的处理了,相信大家对删除数据和填补空值已经不陌生了,下面我们就用一个小练习结束这次的分享。 请找出下面数据中高于170cm的人的信息,不能有重复数据:
from pandas import Series,DataFrame
import pandas as pd
dic1 = {'ID':[1001,1002,1003,1009,1004,1005,1006,1004,1007,1008],
'name':['张三','李四','王五','张三','赵六','孙七','周八','赵六','吴九','郑十'],
'age':['18','19','30','18','20','22','25','20','21','16'],
}
dic2 = {
'high':['150.00','167.00','180.00','150.00','190.00','165.00','168.00','190.00','172.00','178.00'],
'sex':['男','女','男','男','女','男','男','女','女','男']
}
df1=pd.DataFrame(data = dic1,
index = ['a','b','c','d','e','f','g','h','i','j'])
df2=pd.DataFrame(data = dic2,
index = ['a','b','c','d','e','f','g','h','i','j'])
首先,我们可以用iterrows()方法将DataFrame类的内容抄写为一行一行的形式,并且不会影响df的内容。其返回值是行索引和一行的内容; 然后,我们判断每一行数据的身高是否大于170cm,将不满足条件的数据行号加以记录; 最后我们删除记录下的行,并将剩下的部分重复内容删除:
insuff=[]
for index,per in df.iterrows():
if float(per['high'])<170.0:
insuff.append(index)
df.drop(labels=insuff,axis=0,inplace=True)
df.drop_duplicates(inplace=True)
print(df)
本次的内容就这么多了,感谢大家的支持~
|