[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

elang發表於2018-07-30

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

15 年前,軟體開發人員只需掌握很少的一些技能,他或她就有機會獲得 95% 的工作機會。這些技能包括:

  • 物件導向程式設計
  • 指令碼語言
  • JavaScript 以及其他
  • SQL

當您需要快速瀏覽一些資料並得出初步結論時,SQL 是一種常用的工具,這些結論可能會產生一個分析報告或者是編寫一個應用程式。這被稱之為 探索性分析

現如今,資料會以各種各樣的形式出現,不再僅僅是“關係型資料庫”的同義詞。您的資料可能會是 CSV 檔案、純文字、Parquet、HDF5,或者其他什麼格式。這些正是 Pandas 庫的亮點所在。

什麼是 Pandas?

Pandas,即 Python 資料分析庫(Python Data Analysis Library),是一個用於資料分析和處理的 Python 庫。它是開源的,被 Anaconda 所支援。它特別適合結構化(表格化)資料。有關更多資訊,請參考 pandas.pydata.org/pandas-docs…

使用它可以做什麼?

之前您在 SQL 裡面進行的查詢資料以及其他各種操作,都可以由 Pandas 完成!

太好了!我要從哪裡開始呢?

對於已經習慣於用 SQL 語句來處理資料問題的人來說,這是一個令人生畏的部分。

SQL 是一種 宣告式程式語言en.wikipedia.org/wiki/List_o…

使用 SQL,你通過宣告語句來宣告想要的內容,這些宣告讀起來幾乎就如同普通英文短句一樣順暢。

Pandas 的語法與 SQL 完全不同。在 pandas 中,您對資料集進行處理,並將它們鏈在一起,以便按照您希望的方式進行轉換和重構。

我們需要一本 phrasebook(常用語手冊)!

剖析 SQL 查詢

SQL 查詢由幾個重要的關鍵字組成。在這些關鍵字之間,新增您想要看到的具體資料。下面是一些沒有具體資料的查詢語句的框架:

SELECT… FROM… WHERE…

GROUP BY… HAVING…

ORDER BY…

LIMIT… OFFSET…

當然還有其他命令,但上面這些是最重要的。那麼我們如何將這些命令在 Pandas 實現呢?

首先,我們需要向 Pandas 裡面載入一些資料,因為它們還沒有在資料庫中。如下所示:

import pandas as pd

airports = pd.read_csv('data/airports.csv')
airport_freq = pd.read_csv('data/airport-frequencies.csv')
runways = pd.read_csv('data/runways.csv')
複製程式碼

我的資料來自 ourairports.com/data/

SELECT, WHERE, DISTINCT, LIMIT

這是一些 SELECT 語句。我們使用 LIMIT 來擷取結果,使用 WHERE 來進行過濾篩選,使用 DISTINCT 去除重複的結果。

SQL Pandas
select * from airports airports
select * from airports limit 3 airports.head(3)
select id from airports where ident = 'KLAX' airports[airports.ident == 'KLAX'].id
select distinct type from airport airports.type.unique()

使用多個條件進行 SELECT 操作

我們將多個條件通過符號 & 組合在一起。如果我們只想要表格列中條件的子集條件,那麼可以通過新增另外一對方括號來表示。

SQL Pandas
select * from airports where iso_region = 'US-CA' and type = 'seaplane_base' airports[(airports.iso_region == 'US-CA') & (airports.type == 'seaplane_base')]
select ident, name, municipality from airports where iso_region = 'US-CA' and type = 'large_airport' airports[(airports.iso_region == 'US-CA') & (airports.type == 'large_airport')][['ident', 'name', 'municipality']]

ORDER BY(排序)

預設情況下,Pandas 會使用升序排序。如果要使用降序,請設定 asending=False。

SQL Pandas
select * from airport_freq where airport_ident = 'KLAX' order by type airport_freq[airport_freq.airport_ident == 'KLAX'].sort_values('type')
select * from airport_freq where airport_ident = 'KLAX' order by type desc airport_freq[airport_freq.airport_ident == 'KLAX'].sort_values('type', ascending=False)

IN… NOT IN(包含……不包含)

我們知道了如何對值進行篩選,但如何對一個列表進行篩選呢,如同 SQL 的 IN 語句那樣?在 Pandas 中,.isin() 操作符的工作方式與 SQL 的 IN 相同。要使用否定條件,請使用 ~

SQL Pandas
select * from airports where type in ('heliport', 'balloonport') airports[airports.type.isin(['heliport', 'balloonport'])]
select * from airports where type not in ('heliport', 'balloonport') airports[~airports.type.isin(['heliport', 'balloonport'])]

GROUP BY, COUNT, ORDER BY(分組)

分組操作很簡單:使用 .groupby() 操作符。SQL 和 pandas 中的 COUNT 語句存在微妙的差異。在 Pandas 中,.count() 將返回非空/非 NaN 的值。要獲得與 SQL COUNT 相同的結果,請使用 .size()

SQL Pandas
select iso_country, type, count(*) from airports group by iso_country, type order by iso_country, type airports.groupby(['iso_country', 'type']).size()
select iso_country, type, count(*) from airports group by iso_country, type order by iso_country, count(*) desc airports.groupby(['iso_country', 'type']).size().to_frame('size').reset_index().sort_values(['iso_country', 'size'], ascending=[True, False])

下面,我們對多個欄位進行分組。Pandas 預設情況下將對列表中相同欄位上的內容進行排序,因此在第一個示例中不需要 .sort_values()。如果我們想使用不同的欄位進行排序,或者想使用 DESC 而不是 ASC,就像第二個例子那樣,那我們就必須明確使用 .sort_values()

SQL Pandas
select iso_country, type, count(*) from airports group by iso_country, type order by iso_country, type airports.groupby(['iso_country', 'type']).size()
select iso_country, type, count(*) from airports group by iso_country, type order by iso_country, count(*) desc airports.groupby(['iso_country', 'type']).size().to_frame('size').reset_index().sort_values(['iso_country', 'size'], ascending=[True, False])

其中使用 .to_frame()reset_index() 是為什麼呢?因為我們希望通過計算出的欄位(size)進行排序,所以這個欄位需要成為 DataFrame 的一部分。在 Pandas 中進行分組之後,我們得到了一個名為 GroupByObject 的新型別。所以我們需要使用 .to_frame() 把它轉換回 DataFrame 型別。再使用 .reset_index(),我們重新進行資料幀的行編號。

HAVING(包含)

在 SQL 中,您可以使用 HAVING 條件語句對分組資料進行追加過濾。在 Pandas 中,您可以使用 .filter() ,並給它提供一個 Python 函式(或 lambda 函式),如果結果中包含這個組,該函式將返回 True

SQL Pandas
select type, count(*) from airports where iso_country = 'US' group by type having count(*) > 1000 order by count(*) desc airports[airports.iso_country == 'US'].groupby('type').filter(lambda g: len(g) > 1000).groupby('type').size().sort_values(ascending=False)

前 N 個記錄

假設我們做了一些初步查詢,現在有一個名為 by_country 的 dataframe,它包含每個國家的機場數量:

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

在接下來的第一個示例中,我們通過 airport_count 來進行排序,只選擇數量最多的 10 個國家。第二個例子比較複雜,我們想要“前 10 名之後的另外 10 名,即 11 到 20 名”:

SQL Pandas
select iso_country from by_country order by size desc limit 10 by_country.nlargest(10, columns='airport_count')
select iso_country from by_country order by size desc limit 10 offset 10 by_country.nlargest(20, columns='airport_count').tail(10)

聚合函式(MIN,MAX,MEAN)

現在給定一組 dataframe,或者一組跑道資料:

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

計算跑道長度的最小值,最大值,平均值和中值:

SQL Pandas
select max(length_ft), min(length_ft), mean(length_ft), median(length_ft) from runways runways.agg({'length_ft': ['min', 'max', 'mean', 'median']})

您會注意到,使用 SQL 查詢,每個統計結果都是一列資料。但是使用 Pandas 的聚集方法,每個統計結果都是一行資料:

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

不用擔心 — 只需將 dataframe 通過 .T 進行轉換就可以得到成列的資料:

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

JOIN(連線)

使用 .merge() 來連線 Pandas 的 dataframes。您需要提供要連線哪些列(left_on 和 right_on)和連線型別:inner(預設),left(對應 SQL 中的 LEFT OUTER),right(RIGHT OUTER),或 OUTER(FULL OUTER)。

SQL Pandas
select airport_ident, type, description, frequency_mhz from airport_freq join airports on airport_freq.airport_ref = airports.id where airports.ident = 'KLAX' airport_freq.merge(airports[airports.ident == 'KLAX'][['id']], left_on='airport_ref', right_on='id', how='inner')[['airport_ident', 'type', 'description', 'frequency_mhz']]

UNION ALL and UNION(合併)

使用 pd.concat() 替代 UNION ALL 來合併兩個 dataframes:

SQL Pandas
select name, municipality from airports where ident = 'KLAX' union all select name, municipality from airports where ident = 'KLGB' pd.concat([airports[airports.ident == 'KLAX'][['name', 'municipality']], airports[airports.ident == 'KLGB'][['name', 'municipality']]])

合併過程中想要刪除重複資料(等價於 UNION),你還需要新增 .drop_duplicates()

INSERT(插入)

到目前為止,我們一直在講篩選,但是在您的探索性分析過程中,您可能也需要修改。如果您想新增一些遺漏的記錄你該怎麼辦?

Pandas 裡面沒有形同 INSERT 語句的方法。相反,您只能建立一個包含新記錄的新 dataframe,然後合併兩個 dataframe:

SQL Pandas
create table heroes (id integer, name text); df1 = pd.DataFrame({'id': [1, 2], 'name': ['Harry Potter', 'Ron Weasley']})
insert into heroes values (1, 'Harry Potter'); df2 = pd.DataFrame({'id': [3], 'name': ['Hermione Granger']})
insert into heroes values (2, 'Ron Weasley');
insert into heroes values (3, 'Hermione Granger'); pd.concat([df1, df2]).reset_index(drop=True)

UPDATE(更新)

現在我們需要修改原始 dataframe 中的一些錯誤資料:

SQL Pandas
update airports set home_link = 'http://www.lawa.org/welcomelax.aspx' where ident == 'KLAX' airports.loc[airports['ident'] == 'KLAX', 'home_link'] = 'http://www.lawa.org/welcomelax.aspx'

DELETE(刪除)

從 Pandas dataframe 中“刪除”資料的最簡單(也是最易讀的)方法是將 dataframe 提取包含您希望保留的行資料的子集。或者,您可以通過獲取行索引來進行刪除,使用 .drop() 方法刪除這些索引的行:

SQL Pandas
delete from lax_freq where type = 'MISC' lax_freq = lax_freq[lax_freq.type != 'MISC']
lax_freq.drop(lax_freq[lax_freq.type == 'MISC'].index)

Immutability(不變性)

我需要提及一件重要的事情 — 不可變性。預設情況下,大部分應用於 Pandas dataframe 的操作符都會返回一個新物件。有些操作符可以接收 inplace=True 引數,這樣您可以繼續使用原始的 dataframe。例如,以下是一個就地重置索引的方法:

df.reset_index(drop=True, inplace=True)
複製程式碼

然而,上面的 UPDATE 示例中的 .loc 操作符僅定位需要更新記錄的索引,並且這些值會就地更改。此外,如果您更新了一列的所有值:

df['url'] = 'http://google.com'
複製程式碼

或者新增一個計算得出的新列:

df['total_cost'] = df['price'] * df['quantity']
複製程式碼

這些都會就地發生變化。

更多!

Pandas 的好處在於它不僅僅是一個查詢引擎。你可以用你的資料做更多事情,例如:

  • 以多種格式輸出:
df.to_csv(...)  # csv file
df.to_hdf(...)  # HDF5 file
df.to_pickle(...)  # serialized object
df.to_sql(...)  # to SQL database
df.to_excel(...)  # to Excel sheet
df.to_json(...)  # to JSON string
df.to_html(...)  # render as HTML table
df.to_feather(...)  # binary feather-format
df.to_latex(...)  # tabular environment table
df.to_stata(...)  # Stata binary data files
df.to_msgpack(...)	# msgpack (serialize) object
df.to_gbq(...)  # to a Google BigQuery table.
df.to_string(...)  # console-friendly tabular output.
df.to_clipboard(...) # clipboard that can be pasted into Excel
複製程式碼
  • 繪製圖表:
top_10.plot(
    x='iso_country', 
    y='airport_count',
    kind='barh',
    figsize=(10, 7),
    title='Top 10 countries with most airports')
複製程式碼

去看看一些很不錯的圖表!

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

  • 共享:

共享 Pandas 查詢結果、繪圖和相關內容的最佳媒介是 Jupyter notebooks(jupyter.org/)。事實上,有些人(比如傑克·範德普拉斯(Jake Vanderplas),他太棒了)會把整本書都發布在 Jupyter notebooks 上:github.com/jakevdp/Pyt…

很簡單就可以建立一個新的筆記本:

pip install jupyter
jupyter notebook
複製程式碼

之後:

  • 開啟 localhost:8888
  • 點選“新建”,並給筆記本起個名字
  • 查詢並顯示資料
  • 建立一個 GitHub 倉庫,並新增您的筆記本到倉庫中(字尾為 .ipynb 的檔案)。

GitHub 有一個很棒的內建檢視器,可以以 Markdown 的格式顯示 Jupyter notebooks 的內容。

現在,你可以開始你的 Pandas 之旅了!

我希望您現在確信,Pandas 庫可以像您的老朋友 SQL 一樣幫助您進行探索性資料分析,在某些情況下甚至會做得更好。是時候你自己動手開始在 Pandas 裡查詢資料了!

[譯] 如何使用 Pandas 重寫你的 SQL 查詢以及其他操作

✉️ Subscribe to CodeBurst’s once-weekly Email Blast, ? Follow CodeBurst on Twitter, view ?️ The 2018 Web Developer Roadmap, and ?️ Learn Full Stack Web Development.

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章