Pandas是一款適用很廣的數據處理的組件,若是未來從事機械學習或者數據分析方面的工做,我們估計70%的時間都是在跟這個框架打交道。那你們可能就有疑問了,心想這個破玩意兒值得花70%的時間嗎?咱不是還有很牛逼的Tensorflow, keras,神經網絡,classification等等這些牛逼的技術(詞彙)都沒學習呢,咋忽然冒出來一個pandas就要在機器學習中佔了大部分精力去處理呢?其實啊,同窗們,什麼TensorFlow, Keras,神經網絡, 隨機森林啥的,看起來牛氣哄哄的高大上的詞彙,其實都是紙老虎,那些大部分都是封裝的的接口,在實際應用的開發中,基本都是固定模式,主要就是調調參數而已(真正的底層算法研究的除外哈),固然這並非你懶惰的理由,你至少仍是要了解算法原理的,譬如:gradient descent,求偏導等這些基本的概念我們這些小白仍是得有滴。其實我們在機器學習的應用開發中,絕大部分是在作數據處理的工做,於是數據處理工做的質量直接就關係到我們整個應用的質量,因此這是咱們在機器學習中的重中之重,請你們務必重視,下面的每一行代碼,最好你們都要有實踐才行。由於Pandas的內容很是多,因此這篇博文的篇幅會很長很長。。。。。。。。。。。。哈哈,你們有點耐心哈。還有一點,這一節的內容是後面feature engineering(特徵工程)的基礎,大家若是有心要從事機器學習,大家也必需要吃透這節內容的每個知識點(很殘酷的現實,對不對,,哈哈,逃不掉的)。算法
首先,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,以下所示:api
#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結構。她的返回結果以下app
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, 一切輕鬆搞定:dom
#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()等等,隨便你。上面這些就是一些最基礎也是最經常使用的一些數據讀寫功能。
根據上面的結構分析,我們能夠看出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。
咱們知道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 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一般是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是講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的強大。。。。
在後面的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')