如何與pandas愉快地玩耍(一)
pandas和NumPy是我目前吃飯的工具,嗯,對,就相當於」筷子「和」勺子「,尤其是pandas,全稱Python Data Analysis Library,是基於NumPy的工具,名稱來源於panel data 和 data analysis,個人感覺比NumPy更容易上手。本篇文章從應用場景出發,不再單純介紹pandas語法,更具有應用目的性。
導入pandas包並簡化命名為pd(約定俗成的,如果一定要特立獨行,也可以有其他的別名...)
import pandas as pdimport numpy as np #偶爾需要numpy的輔助,這裡先導入為敬
pandas提供創建數據的方法—— 例如,
pd.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
或者
pd.DataFrame.from_dict(data, orient=columns, dtype=None)
但一般情況下,我們是從外部讀取數據的,而不是手動創建,接下來介紹一下數據讀取的內容。
數據讀取
如果數據文件格式是.txt,當然可以選擇with open方式,如下:
with open(data.txt,r,encoding=utf-8) as f: data = f.read()#r是「只讀」模式,根據所需操作不同,也可以替換成"rb","a+"等,不再贅述#encoding也可以根據情況不同,指定為"gbk",目前我常用的編碼就是"utf-8"和"gbk"#也可以按行讀取,data = f.readlines()
然後,pandas文件讀取的強大,老鐵,了解一下?
讀取.txt格式
data = pd.read_table(data.txt,encoding=utf-8)#如果文件路徑或者文件名包含中文,path單引號前加r,然後指定engine=pythondata = pd.read_table(rD:/data_files/待清洗數據/我的數據.txt,engine=python,encoding=gbk)
讀取.csv格式
data = pd.read_csv(data.csv)
讀取excel格式
如果excel文件只有一張sheet有內容或者需要指定讀取某一張sheet內容,比如總理問答「一帶一路」模塊的內容
data = pd.read_excel(r總理問答.xlsx,sheet_name=一帶一路)
或者,可以醬紫:
data = pd.ExcelFile(r總理問答.xlsx)data.parse(一帶一路)
總理問答有很多模塊,很多張sheets,也不怕
data = pd.read_excel(r總理問答.xlsx,sheet_name=[0,3,5]) #指定讀取第1、4、6張sheet表
如果需要獲取每張sheet的名稱再指定讀取,可以醬紫
data = pd.ExcelFile(r總理問答.xlsx)print(data.sheet_names)data.parse([一帶一路,老有所養,住有所居])
如果你想讀取全部的sheets內容併合並處理,可以用for循環呀
file = pd.ExcelFile(r總理問答.xlsx)data = pd.DataFrame() #構造一個空的的DataFramelist = [] #構造一個空的listname_list = file.sheet_namesfor name in name_list: df = pd.read_excel(r總理問答.xlsx,sheet_name=%s % name) list.append(df)data = pd.concat(list,ignore_index=True) #多個sheets的表合併到一起,並處理index
讀取sql
舉一個本地讀取的栗子呦
假設資料庫名稱為mydata,表名稱為taob,
import pandas as pdimport pymysqlconn = pymysql.connect(host = 127.0.0.1,user = root,passwd = 123456,db = mydata,charset = utf8)sql = select * from taob #查詢整表內容的sql語句dt = pd.read_sql(sql,conn)
當然還有pd.read_html等操作,平時不常用,感興趣可以自行探索
我工作中操作最多的就是dataframe對象,接下來主要介紹常用的pandas操作
查看數據
數據讀取後的第一件事,我都是先觀察一下數據是否正確讀取進來了,包含哪些欄位或者是否有編碼問題,偶爾看似讀取成功,實際上是亂碼,這在讀取中文內容數據表的時候,是經常會發生的
df.head() #顯示前五行df.tail() #顯示後五行df.head(3) #查看前三行df.shape #數據維度,意思是「幾行幾列」df.info #數據表信息,包括RangeIndex, Data columns, dtypes, memory usage等df.columns #查看列名df.index #查看數據框的索引df.dtypes #查看數據格式df.describe() #描述性統計
數據選擇和定位
在數據表中,除了對整表的操作,我們更多的是對指定數據操作,比如某些列、某些行,這時,對於指定數據的選擇和定位就頗為重要了
df[comment] #選擇某列的兩種方法df.commentdf[1:4] #選擇2、3、4行df[20180410:20180413] #當有時間index的時候,可以用醬紫的行標籤
除了上面的方法,下面要介紹一對兒「好兄弟」—— loc和iloc,在做條件篩選的時候,我用的都是他們啦
df.loc[0:2,:] #選取前兩行、所有列的內容df.loc[:,[B:D]] #選取所有行,B列到D列的內容df.loc[0:2,[A,C]] #選取前兩行的A、C兩列的內容df.iloc[3] #選取第四行的數據df.iloc[3:5,1:4] #選取第4、5行,第2、3、4列的內容df.iloc[[1,3,5],[2,4]] #選取第2、4、6行的第3、5列的內容df.iloc[1,1] #選取第二行、第二列的值
雖然可完成的操作是相同的,但iloc和loc有那麼一丟丟的區別:loc是根據dataframe的具體標籤或名稱選取列,而iloc是根據標籤所在的位置,從0開始計數。
另外,還有at和iat,ix等,待大家技幾發掘~
修改欄位名稱
如果欄位名稱設置不合理或者中英文摻雜導致名稱很長,為了便於後續處理操作,我們傾向於修改欄位名稱,舉個栗子,如果我想要將"年齡age"欄位名稱修改為"age":
df.rename(columns={年齡age:age}, inplace=True) #需要加inplace=True,否則這個修改只是暫時的
如果想要多個欄位同時修改:
df.rename(columns={年齡age:age,城市city:city}, inplace=True)
觀察缺失值
整表觀察
df.isnull() #缺失值會返回True,非缺失值會返回False
觀察指定欄位缺失值
df[price].isnull()
比較實用的還是以下幾種方法,
df.apply(lambda x:sum(x.isnull()),axis=0)
用到了匿名函數lambda,可以統計每個欄位的缺失值數量
還有一種從盆友那裡學來的代碼:
cols = list(df.columns)missing = list(map(lambda x:df[x].isnull().sum(),cols))missing_dict = dict(zip(cols,missing))missing_dict
很多盆友觀察到缺失值之後,就想到了處理缺失值,我覺得處理缺失值是一個十分重要的環節,有刪除法、平均值/中位數/眾數填充法、擬合法等,要視具體情況來確定處理方法,所以,不要急,先緩緩
觀察欄位唯一值
觀察指定欄位唯一值,一般用於分類變數,連續型變數觀察意義不大
df[city].unique()
把盆友的代碼中的 isnull() 方法改一下(完全是因為示例數據少,不然連續型變數列舉出來讓你分分鐘爆炸...)
cols = list(df.columns)missing = list(map(lambda x:df[x].unique(),cols))missing_dict = dict(zip(cols,missing))missing_dict
還有一種灰常簡單、相當常用的方法,可以同時統計出值出現的頻次
df[品牌].value_counts()
處理缺失值
如果樣本量非常大,存在缺失值的記錄占較少的比例且非重要研究對象,我們可以採用刪除含有缺失值的對象的方法處理,例如,
df.dropna(axis=0, how=any, thresh=None, subset=None, inplace=False)#axis=1 刪除含有缺失值的列,axis=0 刪除含有缺失值的記錄(更常用)#how=any 只要有缺失值就刪除,how=all 當全部值都缺失才刪除
如果缺失值佔總體的比例比較大或者有特殊研究價值,無法輕易刪除,我們可以嘗試用固定無意義內容替代缺失值,比如,
df.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
對於字元串缺失,我通常用「missing」或者「無」去填充;對於數值類缺失值的固定填充,常用「0」或者「-1」,要視情況而定,比如銷量,填充「0」就不太合適,因為正常情況下是有可能出現銷量為0的情況,我們可以選擇較為特殊的"-1",此處希望大家靈活處理
df.fillna(missing,inplace=True)df.fillna(-1,inplace=True)
如果我們發現缺失值有批量規律,比如A列的缺失值都應該為0,B列的缺失值都應該為1,我們可以指定內容替代缺失值,比如,
values = {A: 0, B: 1, C: 2, D: 3}df.fillna(value=values) #可以用limit限制作用的記錄數
我們還可以用統計值替代缺失值,比如平均值/中位數/眾數等
df.fillna(df.mean())
也可以指定相鄰值替代缺失值,比如上/下一個非缺失項,這裡需要用到method方法
df.fillna(method=ffill) #用前面的值填充,可以用limit限制替代數目df.fillna(method=bfill) #用後面的值填充
處理重複值
在觀察數據的過程中,如果重複值讓你很困擾,比如微博水軍用相同內容瘋狂重複評論,沒有統計意義,看一遍就夠夠的了,可以選擇刪除重複值,在這之前,可以先進行重複值觀察,
df.duplicated(subset=None, keep=first) #整表重複值觀察df[content].duplicated(subset=None, keep=first) #指定欄位重複值觀察df.drop_duplicates(subset=None, keep=first, inplace=False) #整表刪除重複值df[content].drop_duplicated(subset=None, keep=first, inplace=False) #指定欄位刪除重複值 #指定欄位重複值觀察
其中,subset參數用來指明「子集」,比如,subset=[id,content] 意思就是當用戶id和內容都重複時,記錄才算重複; keep的意思是保留哪一個值,可以是first,當然也可以是last啦
增加列
除了「唰唰唰」的刪除,增加我也會啊,時常需要從文本內容篩選個關鍵詞啥的,或者用老闆的api打個情緒標籤,首先,你可以用固定值增加一列
df[E] = 100.0
這樣你就獲得了一個所有值都為100.0的列,貌似木有什麼用,誰說的,多學會一種操作,多一個選擇!比如,滿意度指標一般是百分數,老闆需要的是類似96.28%醬紫的格式,而我們數值計算的結果一般是0.962753,「%」先不考慮,最起碼先得乘以100吧,
df[滿意度] = df[滿意度] * 100.0
你也可以
df[滿意度] = df[滿意度] * df[E]
啥?你還想保留兩位小數並且加上百分號?
df[F] = %df[滿意度] = df[滿意度].map(lambda x:round(x,2))df[滿意度] = df[滿意度].astype(str) + df[F]
上面有個騷操作—— astype(str),因為「%」實際上是字元串,我們不能用數值跟字元串相加,所以要先將數值內容轉化為字元串。
還有一個小技巧,可以在指定位置增加列呢
df.insert(loc, column, value, allow_duplicates=False)#例如,要在第四列處增加名為"G"的列,值為0或1,個數就是記錄數len(df)df.insert(3,G,np.random.randint(2,size=len(df)))
刪除列
既然能增加,也可以刪除,可以永久刪除;如果獲取到列名稱,可以指定名稱刪除多個;如果可以得知位於第幾列,可以同時指定第幾列進行刪除(當列名為長長的中文名稱時,用這個方法,你會感謝我的。。。)
del [G] #永久刪除df.drop([E,F],axis=1,inplace=True) #同時指定名稱刪除多個df.drop(df.columns[[0, 1, 3]], axis=1) #指定第幾列進行刪除
啥?你問我如何得知第幾列?
用眼睛看!用手數!......開玩笑啦
list(df.columns).index(滿意度)
這樣返回的值,就是第幾列的列數
修改列和條件篩選
數據的基本操作無非四個字——「增刪改查」,前面我們已經可以對列進行增、刪、查了,那麼「改」呢?改一般不是無意義的修改,都是要按照條件篩選出特定的數據來進行修改的。比如,我有一列連續型數值需要進行分組,150以下為small(S),150~165為medium(M),165~175為large(L),175以上為very large(XL)
df.loc[df[height]<150,height] = Sdf.loc[(df[height]>=150)&(df[height]<165),height] = Mdf.loc[(df[height]>=165)&(df[height]<175),height] = Ldf.loc[df[height]>=175,height] = XL
醬紫,我們就將height列的值,修改為了S, M, L ,XL四種,其中&的含義是「AND」,意思是同時滿足以上條件的才能被篩選出來。
其實,一般不建議在原來列上修改,最好是新建一列操作,然後確認沒有問題後,再刪除原來列,將上面改動一下,我們就創建了一個名為「new_height"的新列
df.loc[df[height]<150,new_height] = Sdf.loc[(df[height]>=150)&(df[height]<165),new_height] = Mdf.loc[(df[height]>=165)&(df[height]<175),new_height] = Ldf.loc[df[height]>=175,new_height] = XL
你聽說過np.where嗎? 當想要"二分"的結果時,這個寫起來特別特別的簡潔,格式為:
df[new_height] = np.where(df[height]>170,Tall,Short)
上面的結果就是,身高高於170就為Tall,否則就為Short,結果存在new_height列中。
除了「AND」之外,肯定還有類似「OR」的用法,比如我們篩選出C列或者是D列大於0的所有數據記錄(cond裡面保存一系列True/False值)
cond = (df[C] > 0) | (df[D] > 0)df.loc[cond]
如果希望在篩選出的數據記錄中,再限定列,那麼,
cond = (df.A > 0)| (df.C > 0)df[[A,C]].loc[cond]
如果有一列名為「D」,裡面所有可能取值為1、2、3、4、5、6,我們取出D值為1,2,3的數據記錄,可用isin()方法
cond = df[D].isin([1,2,3])df.loc[cond]
好了,今天的乾貨我已經儘力「喂」了,歇一下,未完,待續......
推薦閱讀:
TAG:pandas包 |