使用Pandas做資料分析的時候,用的最多的功能恐怕就是對于資料集的索引,選組資料子集。Pandas庫提供了很多非常實用的方法,了解并熟練使用這些方法而不是用for循環的方法将會事半功倍。在這一篇文章中,我們将着重介紹這些方法。
我們将使用2019年全國新能源汽車的銷量資料作為示範資料,資料儲存在一個csv檔案中,讀者可以點選下方下載下傳按鈕下載下傳,也可以在GitHub倉庫下載下傳到 https://github.com/pythonlibrary/practice-pandas-skills.git
本篇文章中會使用到兩個庫pandas 和 numpy,確定它們都正确的安裝,而工作環境則使用jupyter notebook,如果有需要學習如何搭建環境的,可以閱讀 資料科學家的一種工作環境 – virtualenv和Jupyter Notebook。
文章中用于示範的代碼也可以在前邊提到的GitHub倉庫中找到對應的notebook源檔案,檔案名為 index_select_data.ipynb
首先在notebook中導入pandas和numpy,按照常用習慣,pandas導入為pd,numpy導入為np
import
1. 導入資料集
在這一節中,我們會将資料檔案導入為pandas中的資料對象,同時針對這個資料對象,做一些基本的資訊展示,友善我們了解我們将要工作的資料。
我們的原始資料檔案為csv格式,是以可以快速的使用pandas提供的read_csv方法将csv檔案導入為pandas的DataFrame,同時利用DataFrame對象的head方法檢視前兩行資料的内容。
df = pd.read_csv('NEV_sales.csv')
df.head(2)
head方法接收一個整數為參數,代表,我們想要獲得的行數,預設為5行,這裡我們獲得了2行,可以看出,資料的索引(index)是數字,列(column)裡邊包含了品牌以及2019年從1月份到12月份的銷售量。
為了後邊示範,我們将利用df這個資料集再建立一個新的資料集名為df_brand_index,它跟df的差別是,它将使用品牌(brand)作為索引,列為2019年從1月份到12月份的銷售量。我們使用set_index方法來實作。這次,我們使用跟head方法相對應的tail方法來檢視df_brand_index和df的不同。
df_brand_index = df.set_index('brand')
df_brand_index.tail(2)
可以看出,輸出的第一列名稱brand相比其他列名稱向下移了一點,它已經變成了索引,跟原來的索引不同,它具有索引名,即brand,但是所支援的操作方法跟索引是一緻的。
另外,在輸出中我們看到了兩個有趣的資訊,一是,最後一行品牌為總計,這表明我們的原始資料中最後一行是所有行的總計,在真正的資料分析中,它肯定會對結果有不好的影響,是以可以排除掉,而本文側重于pandas的操作,是以它對我們沒影響,二是,奔馳品牌在2019年每個月的銷量都是NaN,NaN是pandas中的一個特殊值,代表缺失值,而在我們這個資料集中,其實也就是銷量為0。
接下來我們從3個方面簡單的看看我們的資料。
首先是資料量,我們正在分析多大的量的資料呢,DataFrame的shape屬性會告訴我們資料有多少行,多少列。通過下述代碼擷取到df_brand_index和df的形狀。
df_brand_index.shape, df.shape
對于df,總過有77行,15列,而df_brand_index有77行,14列因為我們将品牌列轉換為了索引,是以少了一列。
然後是資料類型,我們的資料中是否有一些非法資料類型,我們分析的是新能源車銷量,是以,期望所有的資料都是數字,而非字元串或者其他,DataFrame的dtypes屬性會告訴我們這樣的資訊。
df_brand_index.dtypes
沒問題,所有的列都是數字(因為類型都是float64),如果有任一列出現了 object字樣,那就是說該列包含非數字的内容。
最後是銷售量的基本資訊,例如每個月的銷售量的最大,最小,平均值等等,使用DataFrame的describe()方法可以或者到這些資訊。
df_brand_index.describe()
比如,2019年11月,新能源車在所有品牌的平均銷量為3831台,最大為72795台(不合理,對吧?),為什麼呢,記得我們前邊使用了tail方法看到資料的最後一行為總計,是以這個最大值其實就是總計的值。
2. 列選擇
我們盡量避免使用列索引的稱呼,因為如果看英文文檔的話,pandas并不使用column index來稱呼列,而是直接使用column,如果稱呼列索引的話恐怕會帶來歧義。
通過方括号[]可以從DataFrame擷取到某一列或者某幾列的資料。這裡注意:如果我們擷取多列的資料,則得到的仍然是一個DataFrame,而如果我們僅擷取一列資料,将會得到一個Series(跟DataFrame同一等級的pandas對象,也是pandas中的另外一種常用的資料結構)
sr_brand_index = df_brand_index['2019-11']
sr_brand_index.head(2)
上邊,我們擷取了2019年11月的銷量資料,檢視内容發現,索引名稱還是為品牌,但是列沒有了名稱。
而下方,我們擷取了2019年11月和12月兩個月的銷量資料,檢視内容發現,不僅索引名稱在,同樣的DataFrame具有兩列,分别對應兩個月的資料。
df_brand_index[['2019-12', '2019-11']].head(2)
3. 行選擇
這一節,我們将按行來選擇資料。在pandas中最常用的,也是官方推薦的兩種進行列選擇的方法為loc和iloc,兩者比較容易混淆,這裡按照官方方法提供一個簡單的快速記憶方法loc代表location,使用标簽來定位, 而iloc中的i解讀為integer,即integer location通過數字來定位。什麼意思呢?看下邊對比。
數字Index
首先我們使用df這個DataFrame,還記得吧,這個對象使用數字作為index,索引,我們來使用loc擷取index标簽從0到4的行:
df.loc[0:4]
我們最終得到了5行内容,而index為從0到4.
然後,我們來使用iloc擷取index從位置0到位置4的行:
df.iloc[0:4]
這裡大家發現了差別,我們僅僅得到了4行,index為從0到3,為什麼呢,iloc代表利用整數編号來擷取,其行為類似于python内置資料結構list的操作方法,擷取到的結果為[0,4)。
到這裡,或許讀者已經有點暈了,别着急,看過下邊這個例子以後,你就會恍然大悟,并明白為什麼loc和iloc有時候很容易混淆。
字元串Index
我們再在df_brand_index上邊使用loc和iloc來看看效果,還記得吧df_brand_index中的Index是品牌名稱。
假如像上邊一樣,使用df_brand_index.loc[0:4]來擷取前5行,那麼我們會得到一個異常
為什麼呢?因為loc是使用标簽來做選擇,而這個資料集的Index标簽為字元串而不是數字,正确的用法為:
df_brand_index.loc['北京':'寶駿']
然後iloc的用法就很容易了解并且顯而易見了。
df_brand_index.iloc[0:4]
4. 行+列選擇,找到元素
避免混淆,我們将繼續使用df_brand_index來做示範。假如說現在我們想找到某北汽品牌在2019年11月的銷量,或者前5個品牌在2019年10月到12月的銷量,我們就需要結合行列來一起進行選擇,pandas會智能的根據找到的元素的形狀傳回相應的資料類型,例如:
擷取北汽2019年11月的銷量
df_brand_index.loc['北京', '2019-11']
如果使用iloc方法,下邊的方法可已得到等價的結果
df_brand_index.iloc[0, 1] # 1st column is 2019-11 in df_brand_index
擷取前5個品牌從2019年10月到12月的銷量
df_brand_index.loc['北京':'寶駿', '2019-12':'2019-10']
類似的使用iloc方法,下邊的方法也可以得到等價的結果
df_brand_index.iloc[0:5, 0:3]
5. 條件選擇
另外一個常用的資料選擇篩選辦法是根據元素的内容,比如,我們想擷取到2019年11月和12月銷量均大于3000台的品牌資料。
df_brand_index[(df_brand_index['2019-12'] > 3000) & (df_brand_index['2019-11'] > 3000)]
這裡,我們使用了pandas的布爾選擇功能,即在[]中提供一個布爾條件,(df_brand_index[‘2019-12’] > 3000) & (df_brand_index[‘2019-11’] > 3000)代表,11月和12月銷量均大于3000,隻得特别注意的是,條件中的括号非常重要,是一定需要使用的,否則,pandas将抛出異常。
6. 查找元素位置
最後,在實際項目中還會有需要通過某一個元素的值來尋找它在資料中出現的位置,例如我們想知道2019年11月銷量為6046的品牌,或者我們想知道在整個DataFrame中銷量為6046的所有品牌和對應的月份
在已知列中查找
如果我們想知道2019年11月銷量為6046的品牌,我們完全可以使用第五節中的條件選擇先選取到相應的資料,然後再使用DataFrame的index屬性得到其對應的品牌。
df_brand_index[df_brand_index['2019-11']==6046].index
在整個DataFrame中查找
如果我們想知道在整個DataFrame中銷量為6046的所有品牌和對應的月份,那麼pandas提供的内建方法就無法滿足這個要求了,我們可以借助numpy來快速實作。
numpy提供了where方法,它可以傳回滿足條件的元素在輸入的numpy arry的行列位置編号,通過位置編号就可以在DataFrame中擷取到品牌。
DataFrame中有一個to_numpy方法,可以将DataFrame轉換為numpy array,将兩部分連起來,就可以獲得index和column的編号。
idx = np.where(df_brand_index.to_numpy()==6046)[0][0]
col = np.where(df_brand_index.to_numpy()==6046)[1][0]
idx, col
上邊代碼會輸入(2,1)。其中2為index的編号,1為column編号,對應于df_brand_index中為:
掃碼關注微信公衆号,或用電腦通路網頁以擷取更好的閱讀體驗:https://pythonlibrary.net/