机器学习-Pandas知识点汇总(吐血整理)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
机器学习-Pandas知识点汇总(吐⾎整理)
Pandas是⼀款适⽤很⼴的数据处理的组件,如果将来从事机械学习或者数据分析⽅⾯的⼯作,咱们估计70%的时间都是在跟这个框架打交道。
那⼤家可能就有疑问了,⼼想这个破玩意⼉值得花70%的时间吗?咱不是还有很⽜逼的Tensorflow, keras,神经⽹络,classification等等这些⽜逼的技术(词汇)都没学习呢,咋突然冒出来⼀个pandas就要在机器学习中占了⼤部分精⼒去处理呢?其实啊,同学们,什么TensorFlow, Keras,神经⽹络,随机森林啥的,看起来⽜⽓哄哄的⾼⼤上的词汇,其实都是纸⽼虎,那些⼤部分都是封装的的接⼝,在实际应⽤的开发中,基本都是固定模式,主要就是调调参数⽽已(真正的底层算法研究的除外哈),当然这并不是你懒惰的理由,你⾄少还是要了解算法原理的,譬如:gradient descent,求偏导等这些基本的概念咱们这些⼩⽩还是得有滴。
其实咱们在机器学习的应⽤开发中,绝⼤部分是在做数据处理的⼯作,因⽽数据处理⼯作的质量直接就关系到咱们整个应⽤的质量,所以这是我们在机器学习中的重中之重,请⼤家务必重视,下⾯的每⼀⾏代码,最好⼤家都要有实践才⾏。
因为Pandas的内容⾮常多,所以这篇博⽂的篇幅会很长很长。
哈哈,⼤家有点耐⼼哈。
还有⼀点,这⼀节的内容是后⾯feature engineering(特征⼯程)的基础,你们如果有⼼要从事机器学习,你们也必须要吃透这节内容的每⼀个知识点(很残酷的现实,对不对,,哈哈,逃不掉的)。
Dataframe 和 Series 的结构分析和创建
⾸先,pandas中最重要的两个组成部分就是Dataframe and series。
关于Dataframe咱们就可以把它看成⼀个table(既有row index也有column name 和 values,其本质是⼀个字典dictionary,具体为什么,要看下⽂的分析)。
⽽series⽐dataframe的结构还要简单,她其实就是只有⼀列数据,⽽且他的这⼀列还是没有column name的,她只有这⼀列的values,因⽽在结构上series只有row index和values,series 的本质是⼀个list,具体为什么是list,也是看下⾯的创建过程。
好了,那咱们先⾃定义⼀个dataframe,如下所⽰:
#dataframe allows different index other than 0,1,2,3,4
pd.DataFrame({'A':[434,54],'B':[4,56]},index = [1,2])
咱们看上⾯创建的dataframe对象,⾸先,index(相当于这个table的row number)是可以⾃定义的,你既可以从0开始(默认),也可以从100开始,甚⾄可以是abc。
然后这个dataframe的column name分别是“A”和“B”,这其实相当于这个字典的key值。
说到这⾥,⼤家肯定已经理解了,为什么我上⾯说dataframe的本质是⼀个字典了。
上⾯创建的这个dataframe的结果如下
A B
1 434 4
2 54 56
那么下⾯我们来分析⼀下更加简单的series的结构吧,⾸先咱们先创建⼀个series对象,如下所⽰
#create series with customerized index
pd.Series([4,5,6,7,8,23,54], index=['A','B','C','D','E','F','G'])
和dataframe⼀样,series也是可以⾃定义index,但是series没有column name,它只有values,因⽽可以看出,它的本质是⼀个list结构。
她的返回结果如下
A 4
B 5
C 6
D 7
E 8
F 23
G 54
dtype: int64
可以很明显的看出它的结构。
dataframe和series是pandas的基础,尤其是他们的结构,⼀定要了然于胸,这是pandas这个组合拳的基本功,只有基本功扎实了,才能继续学习更加灵活和瞬息万变的新招式。
读写数据
机器学习,顾名思义就是让机器不断学习之前的经验和数据然后来做出预判。
那么问题来了,我们如何把我们收集到的数据读到内存中来进⾏操作,学过计算机的都知道,计算机运算的时候是通过CPU对内存的数据的操作,那么我们如何将硬盘上的数据,例如:CSV, EXCEL等等这些结构化的数据读⼊我们的内存,并且转换成dataframe呢?⼤家不⽤怕,pandas已经将这⼀系列的io操作转换成⼀句代码就OK了,执⾏调⽤下⾯的api, ⼀切轻松搞定:
#read a csv data from locally
wine_reviews = pd.read_csv("C:\\Users\\tangx\\OneDrive\\Desktop\\DATA\\winemag-data-130k-v2.csv")
#grab the first 5 examples
wine_reviews.head()
上⾯代码的第⼀句话就是讲本地⽂件读出来并且转成dataframe格式赋值给wine_reviews,由于实际中的数据往往⾮常多,因⽽我们通常只截取前5条数据进⾏观察,上⾯的第⼆⾏代码就是通过dataframe.head()的⽅式提取前5条数据。
通过在Spyder中打开wine_reviews变量可以看出,这个数据集⼀共有129971条数据,每条数据有14个特征(feature)。
通过观察上⾯的表格可以看出,系统默认给这个dataframe加了⼀个从0开始的index,但是这张表本来的第⼀列也是从0开始并且递增的数字,因此我们就像让这张表本来的第⼀列作为咱们的index,或者说是row number,咱们可以在加载数据的时候通过加⼀个参数实现,这个参数就是index_col
wine_reviews = pd.read_csv('C:\\Users\\tangx\\OneDrive\\Desktop\\DATA\\winemag-data-130k-v2.csv', index_col = 0)
因为数据的形式不⽌有CSV,也有譬如Excel等,所以pandas在读取数据的时候不⽌有read_csv(), 也有read_excel()等等⼀⼤堆的api供⼤家选择。
在机器学习的应⽤开发的过程中,写数据并不是⼀个常⽤的操作,想想看也是,你总不能把内存的数据写到磁盘中再去处理计算吧,对吧?但是呢,技多不压⾝嘛,咱就顺便把他学习了吧,哈哈,其实也简单的跟⼀⼀样⼀样的,就⼀句代码搞定。
wine_reviews.head().to_csv("C:\\Users\\tangx\\OneDrive\\Desktop\\writedata.csv")
同理,你也可以to_excel()等等,随便你。
上⾯这些就是⼀些最基础也是最常⽤的⼀些数据读写功能。
Indexing and selection
根据上⾯的结构分析,咱们可以看出dataframe就是⼀个table,那么既然是table,在⼀些应⽤场景就肯定会有⼀些需求是获取某⼀个元素,某⼀⾏或者某⼀列的数据,那么这⾥就需要⽤到pandas⾥⾯的index和selection了。
⾸先,咱们先介绍2中常⽤的index的⽅法,他们分别是dataframe.loc[] 和 dataframe.iloc[]. 注意这⾥有⼀个⼩细节,index并不是函数⽅法,咱们都是⽤的⽅括号[],⽽不是括号()。
那么他们到底是什么意思呢?咱们先看⼀下下⾯的代码,咱们先随机创造⼀个8*4的dataframe,它的index和column分别是⽇期和["A","B","C","D"]。
代码如下:
import numpy as np
#help(pd.date_range)
dates = pd.date_range('1/1/2000',periods = 8) #create date from 2000-01-01 to 2000-01-07
df = pd.DataFrame(np.random.randn(8,4), index = dates, columns = ['A','B','C','D']) #assign index and columns to the dataframe
它的返回结果如下
A B C D
2000-01-01 -1.148187 1.584064 -0.589693 -1.403843
2000-01-02 -1.310810 -0.920240 -2.752621 0.913722
2000-01-03 -0.049943 1.280664 -0.353257 -0.023290
2000-01-04 -0.359402 0.350923 -0.455901 -1.747723
2000-01-05 -0.880048 -0.780842 -0.351765 -1.596586
2000-01-06 1.106137 0.419967 -0.409990 -0.513611
2000-01-07 1.348941 1.557287 0.416174 -1.270166
现在我们就来瞧⼀瞧如何⽤loc[] 和 iloc[]。
场景⼀:如果我们要取这个dataframe的第⼀⾏第⼀列的元素,咱们怎么取呢?咱们分别⽤loc[] 和iloc[]来演⽰⼀下:
df.loc['2000-01-01','A']
df.iloc[0,0]
⼤家看出了什么名⽬了没有loc[row, column]和iloc[row_index, column_index] 可以达到同样的效果,都可以查找到指定的数据,上⾯代码返回的数据都是-1.148187。
场景⼆:如何获取某⼀⾏的数据(例如第⼆⾏),咱们可以直接如下所⽰的两种⽅法获取
df.loc['2000-01-01']# returns the first row the the dataframe in the form of series
df.iloc[0]# returns the first row the the dataframe in the form of series
看看我上⾯很有逼格的英⽂注释,⼤家应该也能理解,他们都是返回第⼀⾏数据,但是他们的格式是series,⽽不是list,这⼀点⼤家需要注意哈。
打印他们后的格式如下所⽰
A -1.148187
B 1.584064
C -0.589693
D -1.403843
Name: 2000-01-01 00:00:00, dtype: float64
既然他是series,当然啦,你也可以调⽤⼀个⾮常⽅便的series的api
场景三:如何获取某⼀列的数据(例如第⼆列),国际惯例,咱还是可以通过下⾯的三种种不同的⽅式获取获取
s = df['B']#return a series corresponding the the column labelled 'B'
df.iloc[:,1]
df.loc[:,"B"]
前⾯咱们已经解释了,其实dataframe的本质可以看成⼀个dictionary,因⽽上述第⼀种的⽅式是相当于直接通过key值来获取第⼆列数据。
上⾯的第⼆第三种⽅式是通过loc和iloc的⽅式来获取的。
如果⼤家有看我之前的介绍Numpy的⽂章,⼤家肯定能知道iloc[]其实和Numpy⾥⾯的index⼏乎⼀模⼀样啦。
对了,上⾯代码还是忘记了⼀种获取⼀列的常⽤代码,就是dot operation. 其实很简单就是直接⽤df.B 这⼀⾏代码,也可以获得和上⾯代码⼀样的效果。
上⾯代码的执⾏结果如下:
2000-01-01 -1.148187
2000-01-02 -1.310810
2000-01-03 -0.049943
2000-01-04 -0.359402
2000-01-05 -0.880048
2000-01-06 1.106137
2000-01-07 1.348941
2000-01-08 0.376379
Freq: D, Name: A, dtype: float64
场景四:slicing,分割。
意思就是分割dataframe的⼀部分。
例如从第⼀⾏到第三⾏(不包括)第⼆列到第四列(包括)。
在这种场景下,它的参数形式和Numpy⼏乎是⼀样的,如下所⽰
df.loc['2000-01-01':'2000-01-02','B':'D']
df.iloc[0:2,1:4]
从上⾯可以看出,在slicing的时候,loc[]是既包括开始也包括结尾的index的(简单来说就是包头也包尾巴),⽽iloc[]的索引⽅式是只包括开始的index不包括结尾的index(简单概括就是包头不包尾,这其实也是⼤部分slicing的⽅式)。
这⼀点是他们两种⽅式的⼀点细微不同。
上⾯两⾏代码的返回值是完全⼀样的,如下所⽰:
B C D
2000-01-01 1.584064 -0.589693 -1.403843
2000-01-02 -0.920240 -2.752621 0.913722
返回的也是⼀个dataframe。
数据类型(Data type)
我们知道dataframe是⼀张数据表,既然这张表⾥⾯装的都是数据,那就肯定有不同的数据类型,例如字符串,int,float,boolean等等。
在正式进⼊到数据训练之前,咱⼼⾥必须要清楚的知道这些数据的类型。
这⾥需要知道的⼀点是虽然dataframe⾥⾯的数据的类型可能是千奇百怪的,但是每⼀列的数据都只有⼀种类型。
第⼀咱们来看看通过什么api来获取每⼀列的数据类型。
#grab all the columns data type
all_column_types = wine_reviews.dtypes
她的返回结果是⼀个series,如下所⽰
country object
description object
designation object
points int64
price float64
province object
region_1 object
region_2 object
taster_name object
taster_twitter_handle object
title object
variety object
winery object
dtype: object
第⼆个应⽤场景是获取某⼀列的数据类型(例如咱们想知道price的数据类型),咱们可以通过下⾯的⽅式获得它的数据类型
#grab the type of a column in a dataframe
column_type = wine_reviews.price.dtype
它的返回结果是
dtype('float64')
还有⼀个咱们经常要⽤到的功能是获取dataframe的index和column的名字,咱们可以通过下⾯的代码分别获取到dataframe的index和colum
wine_reviews.index
wine_reviews.columns
她的返回结果是Index的对象,⽽不是list的对象,这个细节⼤家需要注意⼀下。
最后⼀个咱们经常需要⽤到的关于数据类型的功能就是类型转化了(convert,偶尔来个洋⽂装个逼,哈哈)。
在实际操作中,咱们需要经常⽤到将字符串或者bool型的数据转化成INT或者float等,才能在机器学习中进⾏计算,恰恰咱们获取的数据还⼤部分不是int或者float,所以类型转化的应⽤频率还是⾮常⾼的,下⾯来演⽰⼀个将整型int类型转化成float类型的例⼦
#convert a column data type to another type with astype function
wine_reviews.points.astype('float',copy = False)
astype()函数将原来的dataframe中的price的int类型全部转化成了float型。
这⾥咱们就先演⽰这个简单的例⼦,⽽不去演⽰将string转化成int 的例⼦,因为那涉及到了特征⼯程(feature engineering)的内容,咱们在后⾯需要花⼤篇幅讲的,咱们这⾥先卖个关⼦。
所以关于数据类型⽅⾯的知识,pandas中主要就是以上的⼀些⽅法,这些⽅法的最终⽬的其实都是帮助我们更加深刻的理解咱们的数据,相当于打⼀个辅助。
哈哈
calculation functions (翻译过来应该叫做计算函数)
calculation function听起来还挺⾼⼤上的,其实就是pandas的API提供的⼀系列⾮常⽅便的操作函数,例如可以直接获取⼀个series的中位数,平均数,最⼤最⼩数等等这些常见的计算。
其实为了⼤家的⽅便,我已经把⼀些经常⽤到的函数总结在下⾯了,每⼀个函数都有对应的英⽂注释。
这篇⽂章如果能看到这⾥,我相信你们肯定能知道每⼀个函数的作⽤。
这⾥我就不做细节的解释了。
#returns the max value in the series of points
wine_reviews.points.max()
#returns the minimun value in the series of points
wine_reviews.points.min()
#median value of a series(colum
median_points = wine_reviews.points.median()
#mean value of a series(column)
mean_points = wine_reviews.points.mean()
#the index of maximun value in the column
index_max = wine_reviews.points.idxmax()
#the counts of each value in a series, return a series
value_counts = wine_reviews.country.value_counts()
#returns an np array, which includes all the value in a series, and excludes duplicates.
countries = wine_reviews.country.unique()
#returns the counts of each value in series,exclude duplicates
countries_number = wine_reviews.country.nunique()
Apply 和 Map
其实apply和map很像,很多初学者很容易将他们混淆,其实他们有⼀个很明显的不同点,那就是apply通常是element-wise的并且运⽤于整个dataframe,⽽map通常也是element-wise的并且应⽤于series的。
并且apply的参数只能是函数function,⽽map的参数既可以是function 也可以是dictionary和series。
当然啦,series也可以调⽤apply,但是这通常都是在⼀些对series进⾏很复杂的运算的的时候才会调⽤。
记
住,⽆论是apply或者map的参数function,都可以是匿名函数。
下⾯先介绍⼀下map的应⽤。
def isIndia(country):
if country == 'India':
return True
else:
return False
india = wine_reviews.country.map(isIndia)
上⾯的就是先定义⼀个函数来判断它的参数是不是等于"India“, 当你⽤map来调⽤这个函数的时候,就会把series中的每⼀个element都作为参数来传递给isIndia()函数,然后⽤isIndia()函数返回的每⼀个值来替代原来的相对应的值。
最后india的值如下:
0 False
1 False
2 False
3 False
4 False
129966 False
129967 False
129968 False
129969 False
129970 False
Name: country, Length: 129971, dtype: bool
从上⾯的返回值可以看出来,它返回的也是⼀个series。
为了实现上⾯的需求,咱也有另外⼀个⽅式来实现,那就是直接将匿名函数作为参数传递给map()函数。
说实话,匿名函数虽然看起来⽐较⽜逼⾼⼤上,但是实际中我缺不喜欢⽤,因为她的可读性和可维护性都不如上⾯的这种定义函数名的⽅式。
但是为了能显现咱⽜逼,咱还是掌握⼀下⽐较好,免得到时候看不懂被同组的同事鄙视。
哈哈。
下⾯就是⽤匿名函数的⽅式实现上⾯的功能:
US = wine_reviews.country.map(lambda country: True if country == 'US'else False)
先来解释⼀下匿名函数,上⾯lambda关键字就是先声明⼀个匿名函数,紧接着就是这个匿名函数的参数,⼀个冒号:后⾯的就是函数体啦。
上⾯通过⼀个实例展现了map的⼀些⽤法,应该是很简单的,那么接下来来看看dataframe的apply()函数了。
apply()函数其实和map()是⾮常相似的,dataframe调⽤apply的时候,能将dataframe的所⽤元素都作为参数传递给apply()⾥的参数函数,然后逐⼀的返回结果。
她的结果还是⼀个dataframe。
下⾯展⽰⼀个稍微复杂⼀点的情况,就是将⼀个含有多个参数的函数传递给apply()。
def substract_custom_value(x,custom_value):
return x-custom_value
s.apply(substract_custom_value, args = (5,))
看到虽然substract_custom_value函数有两个参数,当dataframe调⽤apply的时候,默认将dataframe的element作为第⼀参数传递给substract_custom_value函数,⽽args的第⼀个元素作为第⼆参数传递给substract_custom_value函数,以此类推。
⼤家千万不要讲参数的数量和顺序弄混了。
上⾯代码的返回结果是讲s⾥⾯的每⼀个元素减去5。
Grouping
Grouping也是数据科学中经常⽤到的⼀个很重要的功能特性。
grouping是讲dataframe按照⼀定的条件分割成⼏个⼩的“dataframe”,这⾥为什么会⽤⼀个双引号呢,是因为grouping以后得到的并不是⼀个个dataframe类型的数据结构,其真正的类型是
core.groupby.groupby.DataFrameGroupBy,因⽽,为了咱们理解它,咱们可以把它看成“dataframe”。
因为它不是真正的dataframe,所以很多dataframe的API它是不能调⽤的。
这⼀点是需要重视的。
下⾯咱们来看看⼀个简单的案例
group_by_points = wine_reviews.groupby('points')
上⾯的⼀⾏简单的代码,就讲wine_reviews这个dataframe分割成很多的“⼩dataframe”——core.groupby.groupby.DataFrameGroupBy,它内部会将相同points的数据整合(group)起来作为⼀个个⼩整体,最后会返回很多的这些⼩整体。
接下来咱们可以对这些⼩
的“dataframe”进⾏很多类似dataframe的操作,例如对他们的series(实际上是core.groupby.generic.SeriesGroupBy)进⾏很多的calculation function,就像正常的dataframe那样。
例如下⾯的这个实例,返回的数据能够很清晰的看出group的结构
wine_reviews.groupby('points').country.value_counts()
为了⽅便看看DataFrameGroupBy的結構,咱们可以直接打印它的结果
points country
80 US 157
Spain 78
Argentina 76
Chile 50
France 15
100 France 8
Italy 4
US 4
Portugal 2
Australia 1
Name: country, Length: 463, dtype: int64
我们从上⾯的结构可以看出group将相同points的数据整理在⼀起形成了⼀个个⼩的数据块。
同时,为了更加精细化的控制,我们经常⽤到应⽤多个条件(conditions)来group,例如,对于dataframe wine_reviews, 咱们可以根据country和Provice两个conditions来进⾏group,如下所⽰
multiple_column_group = wine_reviews.groupby(['country','province'])
上⾯的代码结果就是,即使是同⼀个country,不同的province也是同属于不同的group,它是multiple index,⽽不像上⾯⼀个condition那样,只有⼀个index(group中的index就是你group的那⼀列,⽽不是原来的index了)。
因⽽可以实现更加精细化的控制了,咱们来打印每⼀个group的points的value counts,其结果如下所⽰
multiple_column_group.points.value_counts()
country province points
Argentina Mendoza Province 87 400
86 353
85 349
84 346
88 319
Uruguay Uruguay 89 2
91 2
81 1
82 1
86 1
Name: points, Length: 2914, dtype: int64
上⾯都是分析了group后的⼀些数据的结构,那么这⾥有⼀个问题,如何将group转换回去成为普通的dataframe呢?答案当然是pandas都给咱提供了简单易⽤的API啦,简简单单⼀句话,全部搞定,好了,直接看下⾯代码
regular_index_dataframe =multiple_column_group.reset_index(drop = True)
上⾯⼀句easy的代码,就都OK啦,所以你有时候不得不佩服pandas的强⼤。
Missing values
在后⾯的feature engineering中我会单独好好讲讲missing value,它其实涉及到的知识点还是很多的,这⾥就先介绍⼀下他的基本概念和基础简单的API,⽅便⼤家理解,也是为后⾯真正的特征⼯程打⼀个基础吧。
好了,废话不多说,咱直接进⼊主题了。
所谓的missing value ⼤家都知道,实际中搜寻和挖掘数据的过程中,经常会有⼀些数据丢失或者说是缺失,这些数据有可能会影响咱们的最终模型结果。
所以在训练模型之前,我们有必要先对⼀些缺失的数据进⾏处理和修正。
⾸先咱们得知道某⼀列中是不是有缺失的数据null,咱们可以通过如下的⽅式获得
is_country_nan = wine_reviews.country.isnull() #returns the masks of the series, which valued true if a nan value
上⾯的函数返回⼀个series,这个series是⼀个mask,即如果这条数据的country是空的话,那么返回True, 否则返回False。
如下所⽰
0 False
1 False
2 False
3 False
4 False
129966 False
129967 False
129968 False
129969 False
129970 False
Name: country, Length: 129971, dtype: bool
上⾯只是知道哪些数据的country是空,那么如何将他们选出来呢?放⼼,pandas已经为咱们光⼤的⼈民群众考虑好啦,如下所以,⼀句代码全搞定
#to select nan entries by passing a series of boolean value as a parameters
nan_country_instances = wine_reviews[is_country_nan]
直接将上⾯返回的mask传给dataframe,它就返回了所有country是空的数据。
那么既然找到了⼀些country为空的数据,咱们如何replace这些空的值呢,例如将这些空值NaN都替换成“Unknow”.pandas就是为咱们光⼤屌丝着想哈,都给咱安排的妥妥的了,如下所⽰
#replace NaN to other values
fill_nan_country = wine_reviews.country.fillna('Unknow')
总结:国际惯例,最后来个⼤总结哈。
上⾯从pandas的两个基本组成部分dataframe和series的创建和结构分析开始⼀直到pandas的⼀个⽐较⾼级和常⽤的API应⽤,咱们可以基本的了解pandas的应⽤技巧和⽅法了。
对于从来没有接触过pandas和机器学习的⼩⽩来说,想彻底的理解上⾯的内容和⽅法,还是⽐较困难的(⽜逼的⾼智商除外)。
所以你跟我等凡⼈⼀样,想能熟练的运⽤pandas和深刻的理解它的结构,必须要⽤很多的实践和思考,⾄少把上⾯的代码要逐⼀敲出来并且正确的运⾏出来,才能算是初步的⼊门。
这也是以后要学习特征⼯程的基础,如果你把上⾯的内容都消化了,⾜够你应对机器学习⽅⾯⽤到pandas知识点,其实也就那么回事,在战略上,咱们要藐视它。
后⾯要学习的feature engineering(特征⼯程)才是咱们学习机器学习的核⼼,所以这⼀节的基础⼤家务必夯实。