對(duì)于生成的dDataFrame,下一步進(jìn)行的是對(duì)他的基本操作,增、減、改、查。
一. 數(shù)據(jù)選取
從已有的DataFrame中取出其中一列或幾列,并對(duì)其進(jìn)行操作。
Pandas取出DataFrame的列有兩種方式,兩個(gè)方式?jīng)]有好與壞之分,還是看個(gè)人喜歡用哪個(gè)
- #-*- coding:utf-8 -*-
- import pandas as pd
-
- df = pd.DataFrame({'goods':['coke cola', 'eggplant','condom'], 'quantity':[12,3,1], 'price':[20,12,80]})
- df['goods'] #選擇df的goods這一列
-
- df.goods #同上,是選擇列的第二種方法
需要注意的是,第一種方法看似像是一種列表形式,但是如果像是如下方式書寫代碼
會(huì)報(bào)錯(cuò)- df['goods','quantity'] #會(huì)報(bào)錯(cuò),不要妄想通過這種方式獲取多列。
上方的形式會(huì)引發(fā)一個(gè)KeyError的錯(cuò)誤。如果想通過第一種形式獲取多列,
正確的形式如下 將多列的列名按照一個(gè)list的形式傳入,就可以獲取一個(gè)DataFrame的多列。
二. 數(shù)據(jù)篩選
往往選取出幾個(gè)DataFrame的列并不能滿足篩選,DataFrame的篩選的形式比較特殊,對(duì)列進(jìn)行篩選時(shí)用到的是之前所講的第二種選取列的形式,有點(diǎn)像是把列名視為一個(gè)DataFrame的屬性。
- #-*- coding:utf-8 -*-
- import pandas as pd
-
- df = pd.DataFrame({'goods':['coke cola', 'eggplant','condom'], 'quantity':[12,3,1], 'price':[20,12,80]})
- print df[df.goods =='eggplant'] # goods price quantity
- # 1 eggplant 12 3
上面代碼的意思是選取DataFrame中,
goods列的值為'eggplant‘的所有行。DataFrame中也支持多條件篩選,與Python的判斷語(yǔ)句相同,and、or、not 和 xor分別代表 和、或、非、抑或。也可以用符號(hào)代替 &、|、~、^。每個(gè)判斷條件要
用圓括號(hào)括起來,否則會(huì)報(bào)錯(cuò)
- df[(df.goods =='eggplant') & (df.quantity == 12)]
選取df中 goods列值為 eggplant且 對(duì)應(yīng)的 quantity值為12的所有列。
在篩選數(shù)據(jù)時(shí)候也可以使用loc和iloc函數(shù)實(shí)現(xiàn)子集的選取,意思并不是說上方的篩選語(yǔ)句不對(duì),親測(cè)同樣很好用,而且代碼簡(jiǎn)單了點(diǎn)。loc和iloc進(jìn)行數(shù)據(jù)篩選的格式如下:
- df = df.loc[df.goods=='eggplant', :]
.loc[]相當(dāng)于是把想要篩選的列通過 '
:'全部選出來。loc方法同樣支持多條件篩選。
- df = df.loc[(df.goods =='eggplant') & (df.quantity == 12), :]
loc形式的多條件篩選,條件與條件之間同樣要使用括號(hào)分割開。這兩行l(wèi)oc方法篩選的結(jié)果與之前沒用loc方法寫的代碼效果是一樣的,格式規(guī)范而言肯定是有l(wèi)oc的更容易讓人理解。但是如果只是給自己寫腳本完全可以用前一種,必要時(shí)候做個(gè)注釋即可。
條件篩選的另一種形式,就是選取一個(gè)DataFrame中滿足條件的行的某幾列(并不是全部列)。此時(shí)只需將上面的代碼做一小點(diǎn)改變即可。
- df = df.loc[(df.goods =='eggplant') & (df.quantity == 12), ['goods', 'price']]
此時(shí)親測(cè)使用loc方式會(huì)比較好一些,因?yàn)橹發(fā)oc形式后方的 '
:' 代表的是滿足條件的全部行的所有列的數(shù)據(jù),一個(gè)冒號(hào)囊括了所有列。此時(shí)要是指定其中的某幾列,
通過list的形式,list的元素是DataFrame的列名,元素類型是字符串。上方的代碼所表達(dá)的是滿足條件的所有行的 goods 和 price 兩列。
三. 數(shù)據(jù)的修改
數(shù)據(jù)的修改往往有這幾種形式 ① 針對(duì)某個(gè)值或者某個(gè)范圍的值的修改。 ② 增加列、刪除列 ③ 增加行、刪除行 ④修改索引 ⑤修改列名
數(shù)據(jù)值的修改常見于分類的問題中,比如將一個(gè)列作為分類的列,有時(shí)候往往該列的值不是想要的數(shù)字表示的不同分類,或者更復(fù)雜是一個(gè)連續(xù)變量,此時(shí)針對(duì)DataFrame的修改就格外重要。
添加列
- df['price'][df.price > 15] = 'expensive'
通過這一行代碼,就將df的 price 一列的大于15的商品定義為 ‘expensive’。分類問題中不同的類常常用不同的數(shù)字來表示,而不同類之間的轉(zhuǎn)換需要的就是這樣的篩選與修改。
增加列的方法很簡(jiǎn)單,而且很常用,比如想要在之前的df中添加一個(gè)叫‘remain’的列,記錄是否有貨,可以直接使用下面的代碼:
這樣就在之前的df后面又加了一列‘remain’,但是這一整列的值為‘yes’。如果不想在末尾加列,而是想在某列之間添加一列,那么就需要使用insert方法。
- df.insert(int=0, column = 'remain', value='yes')
int參數(shù)傳入的是一個(gè)整數(shù),這個(gè)整數(shù)是插入列所在的位置,0代表列的索引為0的位置,也就是第一列。 column參數(shù)是設(shè)置列名。 value值是給這列賦值,可以是字符串,如果是字符串,那么這一列的值都會(huì)是這個(gè)字符串;也可以是Pandas的Series,或者是Python的list。這樣可以使得這一整列不是同一個(gè)值。
刪除列
列的刪除使用Pandas模塊中的drop()方法最為高效。假如要?jiǎng)h除剛才添加的remain列。
- df.drop(labels='remain', axis=1, inplace=True)
上方這行代碼會(huì)成功刪除DataFrame中的‘remain’列,注意:
如果inplace參數(shù)不指定為True,只會(huì)在內(nèi)部刪除,df并不會(huì)被真正改變。axis參數(shù)默認(rèn)是0,也就是代表著行,所以
刪除列時(shí)要把a(bǔ)xis改為1。
如果想刪除df中多個(gè)列,drop()方法也是可以的。
- df.drop(labels=['remain', 'quantity'], axis=1, inplace=True)
將想刪除的多個(gè)列名以Python的列表形式傳給labels參數(shù),就可以在該數(shù)據(jù)框中刪除列表中的列。
添加行
Pandas的添加行方式有很多種不過最為直觀的方式是這樣的
- df.loc[3]={'goods':'shampoo','quantity':13, 'price':50}
loc又出現(xiàn),在索引為3的行(第四行)插入一組數(shù)據(jù)。數(shù)據(jù)的形式按照字典的形式傳入。還有一種方法是通過append方法。
- df = df.append({'goods':'shampoo','quantity':13, 'price':50}, ignore_index=True)
也是使用dict形式傳入。
刪除行
- df.drop(labels = 1, axis=0,inplace = True)
刪除索引為1的行,axis=0表示橫向刪除行。刪除多行時(shí),傳入labels的參數(shù)是一個(gè)list,元素是列的索引值。
- df.drop(labels = [1, 4, 6], axis=0,inplace = True)
刪除df中的索引為1,4,6的行。
修改索引
修改索引有兩種情況,改變索引和不改變索引。
① 不改變索引,可以看做是將原始數(shù)據(jù)按照索引順序排序
- df.sort_index(axis=1,ascending=False)
axis表示所有列都隨著索引一起變位置,ascending是降序的意思,默認(rèn)是False。
② 改變索引
set_index()方法
- df.set_index(keys='goods',inplace=True)
將df的goods這一列設(shè)置成索引,在原數(shù)據(jù)框中改。這個(gè)方法感覺更適合之前沒有索引的情況下。當(dāng)之前設(shè)置了索引的情況下需要的是reindex方法。
- df.reindex(new_index,axis=0)
new_index可以是Python列表,元素是現(xiàn)在已有的索引的一部分值,這個(gè)方法常常用于保留想留下的行。
修改列名
修改列名需要用到的是rename()方法。
- dynamic_RNApol.rename(columns={'d0': 'rnapol_d0', 'd1': 'rnapol_d1', 'd3': 'rnapol_d3', 'd5': 'rnapol_d5', 'd7': 'rnapol_d7','d11': 'rnapol_d11', 'd15': 'rnapol_d15', 'd18': 'rnapol_d18', 'ipsc': 'rnapol_ipsc'}, inplace=True)
這就是rename改變列名的用法,傳入的形式是字典,鍵值是新名字;value值是原本的列名。這樣的好處是做到了一一對(duì)應(yīng)。之后一定要加上inplace參數(shù),設(shè)置為TRUE,否則不會(huì)在原有的dataframe中改動(dòng)。
數(shù)據(jù)類型轉(zhuǎn)化
dataframe中的astype()方法是對(duì)數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換的函數(shù)。以一個(gè)例子進(jìn)行說明
- df = pd.DataFrame({'id': range(4), 'age': ['13', '34', '23'], 'weight': ['45.7', '60.9', '55.5']})
- df.dtypes
dtype的結(jié)果會(huì)顯示出,id列的數(shù)據(jù)類型是 int, 而age和weight列的數(shù)據(jù)類型都是 object?,F(xiàn)在將age轉(zhuǎn)化為 int類型,weight轉(zhuǎn)化為float類型。此時(shí)astype函數(shù)的作用就有所體現(xiàn)。
- df.astype({'age':'int', 'weight':'float'})
- valid['distance'] = valid['distance'].astype('int')
通過字典的形式傳入,將兩列轉(zhuǎn)換成了想要的數(shù)據(jù)類型。
數(shù)據(jù)排序
dataframe中索引排序用到的是sort_index方法,相似的列數(shù)值排序運(yùn)用的是sort_values()方法。
- df.sort_values( by=['age'], ascending=False, inplace=True) #只通過age進(jìn)行排序;升序;如有缺失值放在最后。
- df.sort_values(by=['age', 'weight'], ascending=True, inplace=True, na_position='first') #先通過age排序,在其基礎(chǔ)上通過weight排序;降序,缺失值放在最前方。
數(shù)據(jù)的排序在實(shí)際問題中很有用,比如找出聚類中得分最高的幾個(gè)項(xiàng)等等。
數(shù)據(jù)去重
在dataframe中去除重復(fù),使用drop_duplicates()方法。而只是檢查dataframe中是否出現(xiàn)重復(fù)有的是duplicated方法,如果只是想檢查某列中是否存在重復(fù)的值可以通過subset參數(shù)將列傳入到duplicated()方法中。
- df.duplicated() #檢查整個(gè)dataframe中是否出現(xiàn)重復(fù)
- df.duplicated(subset='age') #檢查age這列是否出現(xiàn)重復(fù)數(shù)據(jù)
- df.drop_duplicates() #刪除dataframe中的重復(fù)
其中,subset參數(shù)傳入的可以是單一的列名;同時(shí)也可以是多個(gè)列名組成的list。
四. dataframe基本操作
數(shù)據(jù)抽樣
dataframe的抽樣直接使用sample()方法進(jìn)行,抽樣在涉及到統(tǒng)計(jì)學(xué)算法的時(shí)候很有用,所以先了解一下這個(gè)方法的參數(shù)。
- df.sample(n=5, replace=False)
- df.sample(frac=0.2, replace=True)
這兩種是最常用的形式,n參數(shù)是直接指定抽多少個(gè)樣本,frac參數(shù)則是一個(gè)0到1之間的浮點(diǎn)數(shù),指定的是抽取的樣本占多大比例。replace表示是否是有放回的抽取,默認(rèn)是FALSE。weight參數(shù)在這里沒體現(xiàn),這個(gè)參數(shù)是當(dāng)樣本具有權(quán)重時(shí)候所用的,weight參數(shù)后面加的是列名,未賦值則每列的權(quán)重默認(rèn)為0,如果要賦值權(quán)重,所有權(quán)重的和為1,如果和不為1,則自動(dòng)進(jìn)行歸一化處理。
頻率統(tǒng)計(jì) dataframe中,統(tǒng)計(jì)某列不同的值出現(xiàn)的次數(shù)使用value_counts()方法。
- df = pd.DataFrame({'id': range(4), 'name': ['Jack', 'Craig', 'Chuck', 'Jack'],'gender': ['M', 'M', 'M', 'F'] , 'age': ['13', '34', '23','4'], 'weight': ['45', '60', '55', '30']})
可以看到上面的dataframe中,name列有重名的,現(xiàn)在要統(tǒng)計(jì)不同的名字出現(xiàn)的次數(shù)。
上面的代碼反悔的結(jié)果是在這個(gè)dataframe中,不同的名字出現(xiàn)的次數(shù)。
value_counts()方法的一個(gè)妙用是用于求占比,假如現(xiàn)在要求的是性別的占比。
- df.gender.value_counts()/ sum(df.gender.value_counts)
這行代碼返回的是不同性別人數(shù)除以總?cè)藬?shù)所得到的個(gè)性別的占比。
但是value_counts()方法只能是單變量的計(jì)數(shù)。如果想設(shè)置一個(gè)條件后在進(jìn)行計(jì)數(shù),可以使用的是crosstab()方法。
- pd.crosstab(index=df.name, columns=df.gender)
這行代碼,記錄的是不同名字出現(xiàn)的次數(shù),但是將性別分開討論。columns參數(shù)可以傳入多個(gè)列,傳入形式是list,元素是列名。注意:
columns傳入的列必須是離散型變量,用途是用于分類。
缺失值處理① 缺失值的檢查
檢查缺失值所用的函數(shù)是isnull(),這看起來和MySQL中的差不多。
- print any(df.isnull(), '\n')
檢查整個(gè)dataframe中是否存在缺失值。
- is_null = []
- for col in df.columns:
- is_null.append(any(pd.isnull(df.col)))
這是檢查dataframe每行是否有缺失值。
- is_null = []
- for index in list(df.index):
- is_null.append(any(pd.isnull(df.loc[index, :])))
同理。is_null方法也可以對(duì)每列進(jìn)行缺失值檢查。is_null方法返回的是一個(gè)布爾值,True表示有缺失值,F(xiàn)alse表示沒有缺失值。
② 缺失值的刪除 這是一種比較極端的方法,通過dropna()方法,這個(gè)方法的使用有兩種形式。
這種方式是將有缺失值的行全部刪除。如果在缺失值較多的情況下,數(shù)據(jù)的量會(huì)大打折扣。
這個(gè)是第二種形式,‘a(chǎn)ll’指明了刪除的行是所有值都為缺失值的情況,因?yàn)閿?shù)據(jù)中缺失一些數(shù)據(jù)是很常見的,只刪除全部字段都為空的變量才能真正的保留更多的數(shù)據(jù)。
③ 缺失值的填補(bǔ) 使用fillna()方法可以對(duì)dataframe進(jìn)行缺失值的填補(bǔ)。
- df.fillna(0, inplace=True) #最簡(jiǎn)單的一種方式,缺失值統(tǒng)一以一個(gè)值進(jìn)行填補(bǔ)
- df.fillna(value={'id': 0, 'name': 'No', 'gender': 'unknown', 'age':'unknown', 'weight': 0}, inplace=True) #針對(duì)不同的列設(shè)置不同的缺失填補(bǔ)值
- df.fillna(method='ffill', inplace=True) #用上一行的值對(duì)自身進(jìn)行填補(bǔ)
- df.fillna(method='bfill', inplace=True) #用后一行的值對(duì)自身進(jìn)行填補(bǔ)
- df.fillna({'age': df.age.mean(), 'weight': df.weight.max()}, inplace=True) #用不同列的極值,均值,中位數(shù)等進(jìn)行缺失值填補(bǔ)。
數(shù)據(jù)映射
看見映射,想到的首先是Python中的map()函數(shù),相比于一般的for循環(huán),map函數(shù)具有極高的效率。在日常的Python編程中,map()方法所傳入的是一個(gè)函數(shù)和一個(gè)序列,這個(gè)函數(shù)會(huì)相繼作用于序列中的每一個(gè)元素。得到的是一個(gè)新的序列。dataframe也是一個(gè)特殊的可遍歷序列,但是dataframe的映射所用的是apply()方法。
- is_null = []
- for index in list(df.index):
- is_null.append(any(pd.isnull(df.loc[index])))
現(xiàn)在要挑戰(zhàn)for循環(huán),將原本的檢查每行是否有缺失值的for循環(huán)改成apply()形式。
- isnull = lambda x : any(pd.isnull(df.loc[x,:]))
- is_null = df.apply(func=isnull, axis=1)
func參數(shù)是所用的方法,這里的方法是之前設(shè)定的lambda函數(shù)。axis參數(shù)0代表映射到各列,而1代表著映射到每行。
numpy的某些方法也能通過apply()形式作用于dataframe上,比如說現(xiàn)在有一個(gè)dataframe里面記錄的是學(xué)生的各科成績(jī)(第一列到第五列),現(xiàn)在要計(jì)算每個(gè)學(xué)生的均分,和每個(gè)科目的中位數(shù)。
- df['average'] = df.iloc[:, 0:5].apply(func=np.mean, axis=1)
- df.iloc[:, 0:5].apply(func=np.median, axis=0)