2012 年 Wes McKinney 一本經典的 Python for Data Analysis 由 O'Reilly 出版的。Pandas 參考書 現在有一點過時了 ,他最近宣布新版的新書即將出版 我還是覺得這是一本 瞭解 Pandas 如何作用的必備書 我也欣賞一本更簡要的書
Learning the Pandas Library 由 Matt Harrison 著作 這不是一本有關資料分析及統計的綜合書籍 但如果您只是要學習基本的 Pandas 想要很快的上手
Marco Rodriguez 跟 Tim Golden 維護了一個 很棒的部落格稱為 Planet Python 您可以到 planetpython.org 這個網站, 訂閱 RSS 或者從 推特(Twitter) 的 @PlanetPython 得到最新的文章 里面有很多 Python 資料科學的貢獻者 我也強烈建議您訂閱 RSS
Kyle Polich 運作一個稱為 Data Skeptic 的 podcast 它并不是專門講 Python 但它的制作精良 有很棒的跟這個領域專家座談 以及 簡短的教育課程 大部分的字眼是 有關于機器學習方法 但如果您計畫 進一步探索這個學程 這個課程也在其中, 我真的很建議您訂閱這個 podcast
The Series Data Structure
傳遞一個 list 的值來新創一個 Series, 當這麼做時, Pandas 會自動從零開始分配索引 并將該series的名稱設置為“None”。
傳入一些資料 索引和名稱 資料可以是任何東西,類似陣列(array),像list一樣。
pandas自動的識別了類型 在list中包含的數據,在這裡我們傳入string列表, pandas將這類型設定為object。
In [1]:
import pandas as pd
import numpy as np
In [2]:
animals = ['Tiger', 'Bear', 'Moose','Bear']
pd.Series(animals)
Out[2]:
0 Tiger
1 Bear
2 Moose
3 Bear
dtype: object
In [3]:
numbers = [1, 2, 3]
pd.Series(numbers)
Out[3]:
0 1
1 2
2 3
dtype: int64
不一定要使用strings。 如果傳入整數列表, 可以看見Pandas設定類型為int64。 在Pandas內部儲存series的值,使用NumPy程式庫的類型陣列(typed array)。 在處理數據時這提供顯著的加速相比于傳統 Python的list
NumPy 和 Pandas 如何處理遺失的資料: 在Python中,有none type以表示資料缺失。
In [4]:
animals = ['Tiger', 'Bear', None]
pd.Series(animals)
Out[4]:
0 Tiger
1 Bear
2 None
dtype: object
In [5]:
numbers = [1, 2, None]
pd.Series(numbers)
Out[5]:
0 1.0
1 2.0
2 NaN
dtype: float64
- 事實上不能對Nan本身的做相等測試。 因為答案總是錯誤的。
- 需要使用特殊功能來測試 '不是一個數字'的存在,例如numpy程式庫中的isnan。
In [6]:
np.nan == None
Out[6]:
False
In [7]:
np.nan == np.nan
Out[7]:
False
In [8]:
np.isnan(np.nan)
Out[8]:
True
In [9]:
sports = {'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'}
s = pd.Series(sports)
s
Out[9]:
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
dtype: object
In [10]:
s.index
Out[10]:
Index(['Archery', 'Golf', 'Sumo', 'Taekwondo'], dtype='object')
In [11]:
s = pd.Series(['Tiger', 'Bear', 'Moose'], index=['India', 'America', 'Canada'])
s
Out[11]:
India Tiger
America Bear
Canada Moose
dtype: object
In [12]:
sports = {'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'}
s = pd.Series(sports, index=['Golf', 'Sumo', 'Hockey'])
s
Out[12]:
Golf Scotland
Sumo Japan
Hockey NaN
dtype: objec
- series創建完成后, 可以使用index屬性獲取index對象。
- 也可以將index的創建與數據分離,通過 將index作為列表,明確地傳遞給series。
那麼如果index中的值列表 與dictionary中用于創建該系列的keys不對齊
- pandas會覆蓋自動創建index值,僅只用 你提供的所有的index值。
- 會忽略你的dictionary中所有的keys,當keys不在你的index中,pandas將添加non類型或NaN
Querying a Series
Pandas的Series(列表)可以查詢,使用索引(index)的位置或索引的標簽(label)。
要利用數位位置查詢,從零開始,使用
iloc
屬性。要通過索引標簽(label)進行查詢,可以使用
loc
屬性。以下是維基百科的全國體育賽事數據。假設我們想要列出所有的運動當我們的索引(index),和 國家列表作為值(value)。
sports = {'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'}
s = pd.Series(sports)
s
>>>
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
dtype: object
s.iloc[3]
>>> 'South Korea'
s.loc['Golf']
>>> 'Scotland'
s[3]
>>> 'South Korea'
s['Golf']
>>> 'Scotland'
- 請記住,iloc和loc不是方法(method),是屬性(attribute)。所以不用括號()來查詢它們,而是使用方括號[], 我們稱之為索引運算符。在Python中, 這是獲取(get)和設置(set)一個項目的方法,根據其使用的背景來決定。
這看起來可能有點困惑的,如果你習慣于語言在哪里封裝在里面的 屬性、變數和性能是常見的,比如在JAVA中。 Pandas試圖使我們的程式更具有可讀性,并提供一種 智慧語法,使用index操作符直接在series本身。
最后兩行指令:例如,如果你傳入一個整數參數, 運算子會表現得好像你想要通過iloc屬性來查詢。如果你傳入一個物件(object), 它將認為你想要查詢使用根據標簽(label)的loc屬性。
- 那么如果你的index是整數列表會發生什么呢?這有點復雜,pandas無法自動確定 你是打算通過索引位置或索引標簽進行查詢。所以在series本身使用index操作時,你需要小心。而更安全的選擇是更加明確,直接使用 iloc或loc屬性。
sports = {99: 'Bhutan',
100: 'Scotland',
101: 'Japan',
102: 'South Korea'}
s = pd.Series(sports)
s[0]
#This won't call s.iloc[0] as one might expect, it generates an error instead
一個典型的程式設計方法,要遍歷 該series中的所有項目,并調用一個你感興趣的運算: 例如,我們可以創建一個浮點值的數據組(dataframe)。讓我們把這些看作是不同產品的價格。我們可以寫一個小的例行程序碼,遍歷的所有 series中的項目,并將它們加一起以獲得總數。
Pandas和基礎的NumPy程式庫支持一個稱為 向量化
vectorization.
Vectorization與NumPy庫中的大部分功能一起使用, 包括sum函數。
調用np.sum 并傳入一個可遍歷迭代的項目。在這里,我們的pandas series。
s = pd.Series([100.00, 120.00, 101.00, 3.00])
s
>>>
0 100.0
1 120.0
2 101.0
3 3.0
dtype: float64
total = 0
for item in s:
total+=item
print(total)
>>> 324.0
total = np.sum(s)
print(total)
>>> 324.0
現在這兩種方法產生相同的值,但是哪一種是確實更快嗎?
首先,設置一個大系列的隨機(random)數字。
神奇功能以百分比符號%開頭。如果我們打入這個標志%,然后按Tab鍵, 我們可以看到可用的魔術函數的列表。你也可以編寫自己的魔術函數 但這不過是本課程的范圍之外。我們實際上會
使用所謂的細胞(cellular)魔術函數 -- 以兩個百分比的符號開始, 并修改或包裝當前Jupyter單元中的程式。
** 要使用的函數稱為timeit。你可能已經從名稱猜到了,此函數會運行我們的程式幾次來確定,平均運行時間。
#this creates a big series of random numbers
s = pd.Series(np.random.randint(0,1000,10000))
s.head()
%%timeit -n 100
summary = 0
for item in s:
summary+=item
>>> 100 loops, best of 3: 1.87 ms per loop
%%timeit -n 100
summary = np.sum(s)
>>>100 loops, best of 3: 107 μs per loop
- 在pandas和NumPy的相關的功能稱為廣播(broadcasting)。通過broadcasting,你可以對series中的每個值應用操作, 更改series。
- 例如,如果我們想要對每個隨機變數增加2, 我們可以使用+=運算符號直接在列表對像上快速地執行。在這里,我只需要使用head運算印出前五項 首先,我想要先來介紹這門課的四位講師
- 做這的樣程序方式是,遍歷所有的 列表中的項目和直接增加它的數值。很快的提一下, Pandas確實支持遍歷迭代列表項目,很類似于dictionary, 讓你容易地把數值分拆開。但如果你發現自己反覆遍歷一列表, 你應該質疑你做的方式是否是盡可能最好的。
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
for label, value in s.iteritems():
s.loc[label]= value+2
>>>
10 loops, best of 3: 1.65 s per loop
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
s+=2
>>>?
10 loops, best of 3: 514 μs per loop
- 最后一點要注意的,在使用索引運算來存取列表資料。 .loc屬性(attribute) 不僅可以修改數據, 還可以添加新數據。如果作為索引傳入的值不存在,則它會添加一個新條目。請記住,指數可以有混合類型。雖然重要的是,要注意在下面的類型是什么, Pandas會根據需要,自動更改基本的NumPy類型。
s = pd.Series([1, 2, 3])
s.loc['Animal'] = 'Bears'
s
>>>
0 1
1 2
2 3
Animal Bears
dtype: object
original_sports = pd.Series({'Archery': 'Bhutan',
'Golf': 'Scotland',
'Sumo': 'Japan',
'Taekwondo': 'South Korea'})
cricket_loving_countries = pd.Series(['Australia',
'Barbados',
'Pakistan',
'England'],
index=['Cricket',
'Cricket',
'Cricket',
'Cricket'])
all_countries = original_sports.append(cricket_loving_countries)
original_sports
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
dtype: object
cricket_loving_countries
Cricket Australia
Cricket Barbados
Cricket Pakistan
Cricket England
dtype: object
all_countries
Archery Bhutan
Golf Scotland
Sumo Japan
Taekwondo South Korea
Cricket Australia
Cricket Barbados
Cricket Pakistan
Cricket England
dtype: object
all_countries.loc['Cricket']
Cricket Australia
Cricket Barbados
Cricket Pakistan
Cricket England
dtype: object
- 我們回到我們原來的運動列表。可以創建一個帶有多個條目的新列表,用 板球索引,然后使用append將它們放在一起。使用append時,有幾個重要的注意事項。首先,Pandas將采取你的列表, 并嘗試推斷使用最好的數據類型。在這個例子中,一切都是字符(string),所以這里沒有問題。
- 其次,append方法實際上并沒有改變底層的列表。而是返回一個由兩個附加在一起組成的新列表。我們可以回溯并列印原始列表值, 看到它們沒有改變。
這是實際上的一個重大問題 新的Pandas使用者,之前習慣了物件(objects)在原處更改。所以要當心了,不只是append,還有其他的Pandas函數功能。
最后,我們看到,當我們查詢附加在一起的列表,用板球 作為國家運動的,我們不是得到一個單一的值,而是一個列表。這實際上是很常見的。
The DataFrame Data Structure
DataFrame數據結構是Pandas的核心。
DataFrame在概念上是一個二維列表(series)對象, 其中有一個索引(index)和多列內容,每列(column)都有一個標簽(label)。事實上,列(column)和行(row)之間的區別 實際上只是一個概念上的區別。可以將DataFrame本身視為簡單的雙軸有標簽的陣列。
- 可以以許多不同的方式創建一個DataFrame。例如,你可以使用一組列表(series), 其中每個列表代表一行數據。或者你可以使用一組字典(dictionary), 其中每個字典都代表一行數據。
purchase_1 = pd.Series({'Name': 'Chris',
'Item Purchased': 'Dog Food',
'Cost': 22.50})
purchase_2 = pd.Series({'Name': 'Kevyn',
'Item Purchased': 'Kitty Litter',
'Cost': 2.50})
purchase_3 = pd.Series({'Name': 'Vinod',
'Item Purchased': 'Bird Seed',
'Cost': 5.00})
df = pd.DataFrame([purchase_1, purchase_2, purchase_3], index=['Store 1', 'Store 1', 'Store 2'])
df.head()
>>>
Cost Item Purchased Name
Store 1 22.5 Dog Food Chris
Store 1 2.5 Kitty Litter Kevyn
Store 2 5.0 Bird Seed Vinod
- 與列表(series)類似,可以使用iloc和loc屬性提取數據。因為DataFrame是二維的,所以將單一值傳遞給loc 索引操作將返回一個列表,如果只有一行返回。
df.loc['Store 2']
>>>
Cost 5
Item Purchased Bird Seed
Name Vinod
Name: Store 2, dtype: object
- 列表的名稱作為行索引值返回, 而列名(column_name)也包括在輸出中。