在品味了Pandas數(shù)據(jù)分析庫一段時(shí)間后,我已經(jīng)總結(jié)了很長一個(gè)有用的代碼段的列表,我發(fā)現(xiàn)我會(huì)一次又一次頻繁使用這些代碼段。這些小建議可以節(jié)省很多用來瀏覽Pandas文檔的時(shí)間。
在本文中,我們從將披薩餅訂單數(shù)據(jù)填充dataframe開始。如果你是新接觸Pandas,這里給出一些關(guān)鍵的詞。
Dataframe 數(shù)據(jù)中添加了索引的行和列,類似電子表格或者數(shù)據(jù)庫的表。
Series= 數(shù)據(jù)的單獨(dú)一列
Axis:0==行,1==列
Shape:Dataframe中(行數(shù),列數(shù))
- 導(dǎo)入CSV文件
對(duì)于read_csv函數(shù)有一千種選項(xiàng)可以用來簡(jiǎn)化數(shù)據(jù)的前處理。沒有人想在清洗數(shù)據(jù)上花太多的時(shí)間,所以關(guān)鍵就在于你是否能在導(dǎo)入初始文件的時(shí)候就把事情搞定。
df = pd.read_csv('pizza.csv')
需要解析日期?僅僅需要把處理方法傳遞個(gè)相應(yīng)的列名。
df = pd.read_csv('pizza.csv', parse_dates=['dates'])
只需要某些列?
df = pd.read_csv('pizza.csv', usecols=['foo', 'bar'])
- 在Dataframe中探索數(shù)據(jù)
你想要做的第一件事可能就是數(shù)據(jù)是什么樣的。這里有幾種方法可以檢查Pandas的數(shù)據(jù)。
df.head() #先頭5行
df.tail() #最后5行
df.sample(5) #行的隨機(jī)采樣
df.shape #一個(gè)元組,包含行列數(shù)目
df.describe() #
df.info() #內(nèi)存使用情況和數(shù)據(jù)類型
| order_number | date | size | topping | price | discount | coupon | |
|---|---|---|---|---|---|---|---|
| 0 | PZZA0001 | 08/21/16 | Small | Anchovies | 12.99 | 3.5 | Yes |
| 1 | PZZA0000 | 09/26/16 | Large | Pepperoni | 14.50 | 0.0 | No |
| 2 | PZZA0001 | 09/27/16 | Extra Large | Bell Pepper | 19.99 | 0.0 | No |
| 3 | PZZA0002 | 09/28/16 | Extra Large | Olives | 20.99 | 5.0 | Yes |
| 4 | PZZA0003 | 09/29/16 | Extra Large | Pepperoni | 21.99 | 0.0 | No |
披薩餅數(shù)據(jù)的Dataframe的頭幾行結(jié)果如下:
| order_number | date | size | topping | price | discount | coupon | |
|---|---|---|---|---|---|---|---|
| 0 | PZZA0001 | 08/21/16 | Small | Anchovies | 12.99 | 3.5 | Yes |
| 1 | PZZA0000 | 09/26/16 | Large | Pepperoni | 14.50 | 0.0 | No |
| 2 | PZZA0001 | 09/27/16 | Extra Large | Bell Pepper | 19.99 | 0.0 | No |
| 3 | PZZA0002 | 09/28/16 | Extra Large | Olives | 20.99 | 5.0 | Yes |
| 4 | PZZA0003 | 09/29/16 | Extra Large | Pepperoni | 21.99 | 0.0 | No |
- 在Dataframe中增加一個(gè)新列
簡(jiǎn)單高效的做法就是在Dataframe中定義一個(gè)新列。下面的代碼給我們一個(gè)新列,其中每一行的值都設(shè)為23。通常你需要用一個(gè)數(shù)組或者Series來設(shè)定列的內(nèi)容,這里數(shù)組或者Series的長度必須跟數(shù)據(jù)中的行數(shù)相匹配。
df['new_column'] = 23
需要基于其他列來生產(chǎn)一個(gè)新列?
full_price = (df.price + df.discount)
df['original_price'] = full_price
需要根據(jù)某種順序來生成列?insert方法中第一個(gè)參數(shù)是列的位置。下面的代碼將列放到Dataframe的開始位置。
df.insert(0, 'original_price', full_price)
- 選擇一個(gè)具體單元格的值
所謂的單元格這里指位置的是行列交叉所在的數(shù)據(jù),跟Excel里的單元格是一個(gè)意思。你可能感覺這比較簡(jiǎn)單,但是具體語法卻并不是那么直觀。在Pandas中有3個(gè)方法同時(shí)能做到這件事:.loc, .iloc, .ix——對(duì)新接觸的人而言,這又更加了理解的難度。
通常我都是用.ix,因?yàn)檫@個(gè)方法允許整數(shù)和字符串混合使用。首先輸入行的索引,然后是列的索引。
df.ix[2, 'topping']
你也可以通過點(diǎn)符號(hào)先選擇列,然后給定行的索引,這樣看起來更清爽:
df.topping.ix[2]
上述的兩種方法都會(huì)返回單元格的值:
>>> 'Bell Pepper'
- 通過條件邏輯來篩選DataFrame
假設(shè)我們需要分析在topping列中包含菠蘿的訂單。
filtered_data = df[df.topping == 'pineapple']
或者我們要找的是高于一定價(jià)格線的訂單:
filtered_data = df[df.price > 11.99 ]
如果要同時(shí)滿足上述兩個(gè)條件我們?cè)趺崔k?僅僅將條件包裹成元組并將條件用位運(yùn)算符連接起來就行。
filtered_data = df[(df.price > 11.99) & (df.topping == 'Pineapple')]
現(xiàn)在我們得到了包含菠蘿并且topping價(jià)格超過11.99的所有訂單。
| order_number | date | size | topping | price | discount | coupon |
|---|---|---|---|---|---|---|
| 6 | PZZA0006 | 10/01/16 | Medium Pineapple | 17.50 | 0.0 | No |
| 9 | PZZA0009 | 10/04/16 | Medium Pineapple | 12.99 | 2.0 | Yes |
- 通過某一列來對(duì)DataFrame進(jìn)行排序
下面的代碼意義不言自明,非常有用。
df.sort_values('price', axis=0, ascending=False)
- 對(duì)于列中的每行都應(yīng)用一個(gè)函數(shù)
Python中匿名lambda函數(shù)非常適合這樣的任務(wù)。假設(shè)我們需要用一個(gè)自定義的函數(shù)來計(jì)算DataFrame中每一行的稅金。Pandas中的apply函數(shù)運(yùn)行我們將函數(shù)傳遞進(jìn)來,在列中的每一個(gè)值上使用。在這個(gè)例子中,我們通過在價(jià)格數(shù)據(jù)上執(zhí)行一個(gè)自定義的函數(shù)來抽取出新的稅金特征。
def calculate_taxes(price):
taxes = price * 0.12
return taxes
df['taxes'] = df.price.apply(calculate_taxes)
| order_number | price | taxes | |
|---|---|---|---|
| 0 | PZZA0000 | 12.99 | 1.5588 |
| 1 | PZZA0001 | 14.50 | 1.7400 |
| 2 | PZZA0002 | 19.99 | 2.3988 |
| 3 | PZZA0003 | 20.99 | 2.5188 |
| 4 | PZZA0004 | 21.99 | 2.6388 |
- 通過條件邏輯追加一個(gè)新列
numpy中的where函數(shù)在根據(jù)條件邏輯來抽取特征方面非常有用。讓我們?cè)O(shè)想一下披薩店只有在銷售額度超過15美元時(shí)才有利潤。我們根據(jù)這個(gè)觀點(diǎn)創(chuàng)建一個(gè)新的列:
df['profitable'] = np.where(df['price']>=15.00, True, False)
- 找出復(fù)數(shù)列或者行的均值或者標(biāo)準(zhǔn)差
如果你有一個(gè)DataFrame,在每列中都是相同類型的數(shù)據(jù),比如可能是一定時(shí)間內(nèi)的金融數(shù)據(jù),你可能需要找出水平方向上的均值:
df['mean'] = df.mean(axis=1)
或者找出垂直方向上的標(biāo)準(zhǔn)差:
df.std(axis=0)
- 將DataFrame轉(zhuǎn)換成Numpy數(shù)組
將DataFrame中的數(shù)據(jù)轉(zhuǎn)換成數(shù)組的方式非常簡(jiǎn)單:
df.values
如果你想要保留表的形式:
df.as_matrix
- 用連接的方式組合DataFrames
你可以將行或者列連接到一起,唯一的要求就是在相應(yīng)的軸上具有相同的shape。比如在垂直方向上連接行:
pd.concat([df_1, df_2], axis=0)
或者水平方向上連接列:
pd.concat([df_1, df_2], axis=1)
- 基于一個(gè)索引鍵來組合DataFrames
在Pandas中合并做法跟SQL做法類似。如果你有兩個(gè)DataFrame擁有共同的鍵,比如一個(gè)披薩的‘order_id‘,你可以進(jìn)行內(nèi)連接(inner join),外連接(outer join),左鏈接(left join),右連接(right join),正如你能用SQL做到的一樣。
merged_df = df_1.merge(df_2, how='left', on='order_id')
- 將日期分裂成自己的日,周,月,年列
首先,確認(rèn)數(shù)據(jù)時(shí)日期格式。然后用dt方法來抽取你需要的數(shù)據(jù)。
date = pd.to_datetime(df.date)
df['weekday'] = date.dt.weekday
df['year'] = date.dt.year
- 找出DataFrame中的Nan
計(jì)算Nan出現(xiàn)的總數(shù):
df.isnull().sum().sum()
列舉每個(gè)列中的Nan數(shù):
df.isnull().sum()
- 填充Nan或者缺失數(shù)據(jù)
多數(shù)機(jī)器學(xué)習(xí)算法不喜歡Nan這樣的值,所以你可能需要對(duì)他們進(jìn)行轉(zhuǎn)換。如果topping列有某些值缺失,我們可以用默認(rèn)值來填充。
df.topping = df.topping.fillna('Cheese')
或者我們可以在整個(gè)DataFrame中丟掉具有缺失值的行:
df = df.dropna(axis=0)
- 通過列的groupby函數(shù)抽取特征
對(duì)列進(jìn)行分組是從數(shù)據(jù)抽取特征的很好的方式。當(dāng)你的數(shù)據(jù)可以計(jì)數(shù)或者可以用某種方式進(jìn)行量化,分組就特別有用了。例如,你可以根據(jù)topping列對(duì)披薩分組,然后計(jì)算每組中價(jià)格的均值。
df.groupby('topping')['discount'].apply(lambda x: np.mean(x))
或者可能你想要看一下某個(gè)值出現(xiàn)多少次:
df.groupby('topping')['discount'].apply(lambda x: x.count())
topping
Anchovies 3
Bell Pepper 1
Cheese 2
Olives 1
Pepperoni 3
Pineapple 2
Veggie 1
Name: discount, dtype: int64
- 創(chuàng)建Bins
假設(shè)我們要根據(jù)不同的價(jià)格范圍將訂單歸到3個(gè)不同的桶里。這樣的做法非常適合講擁有噪聲的數(shù)據(jù)簡(jiǎn)化。
bins = [0, 5, 15, 30]
names = ['Cheap', 'Normal', 'Expensive']
df['price_point'] = pd.cut(df.price, bins, labels=names)
| order_number | price | price_point | |
|---|---|---|---|
| 0 | PZZA0000 | 12.99 | Normal |
| 1 | PZZA0001 | 14.50 | Normal |
| 2 | PZZA0002 | 19.99 | Expensive |
| 3 | PZZA0003 | 20.99 | Expensive |
| 4 | PZZA0004 | 21.99 | Expensive |
- 通過循環(huán)生成一個(gè)新列
假設(shè)說我嗎想要將披薩配料分成‘蔬菜’和‘肉’兩類。處理這樣的名稱類值可以用一個(gè)循環(huán)來做到。(注意:你也可以用之前提到的apply函數(shù)來完成這個(gè)任務(wù)。
topping_type = []
for row in df.topping:
if row in ['pepperoni', 'chicken', 'anchovies']:
topping_type.append('meat')
else:
topping_type.append('vegetable')
df['topping_type'] = topping_type
注意:這種方法效率非常低下,應(yīng)該盡量避免。
種類有限的情況下,應(yīng)該盡量使用pandas的indexing方法。
比如:
df[df[['toppings'] == 'chicken']['topping_type'] = 'meat'
- 分塊方法來讀入大規(guī)模數(shù)據(jù)集
有時(shí)你可能要處理一個(gè)很大的文件,超出了你內(nèi)存能夠處理的范圍,讓你的程序崩潰。在這種情況下,你可能需要將整個(gè)文件分成塊來讀入。
chunksize = 500
chunks = []
for chunk in pd.read_csv('pizza.csv', chunksize=chunksize):
# Do stuff...
chunks.append(chunk)
df = pd.concat(chunks, axis=0)