- 原文地址:How to rewrite your SQL queries in Pandas, and more
- 原文做者:Irina Truong
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:geniusq1981
- 校對者:DAA233
15 年前,軟件開發人員只需掌握不多的一些技能,他或她就有機會得到 95% 的工做機會。這些技能包括:html
當您須要快速瀏覽一些數據並得出初步結論時,SQL 是一種經常使用的工具,這些結論可能會產生一個分析報告或者是編寫一個應用程序。這被稱之爲 探索性分析。前端
現現在,數據會以各類各樣的形式出現,再也不僅僅是「關係型數據庫」的同義詞。您的數據可能會是 CSV 文件、純文本、Parquet、HDF5,或者其餘什麼格式。這些正是 Pandas 庫的亮點所在。android
Pandas,即 Python 數據分析庫(Python Data Analysis Library),是一個用於數據分析和處理的 Python 庫。它是開源的,被 Anaconda 所支持。它特別適合結構化(表格化)數據。有關更多信息,請參考 pandas.pydata.org/pandas-docs…。ios
以前您在 SQL 裏面進行的查詢數據以及其餘各類操做,均可以由 Pandas 完成!git
對於已經習慣於用 SQL 語句來處理數據問題的人來講,這是一個使人生畏的部分。github
SQL 是一種 聲明式編程語言:en.wikipedia.org/wiki/List_o…。web
使用 SQL,你經過聲明語句來聲明想要的內容,這些聲明讀起來幾乎就如同普通英文短句同樣順暢。sql
而 Pandas 的語法與 SQL 徹底不一樣。在 pandas 中,您對數據集進行處理,並將它們鏈在一塊兒,以便按照您但願的方式進行轉換和重構。數據庫
咱們須要一本 phrasebook(經常使用語手冊)!編程
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 語句。咱們使用 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() |
咱們將多個條件經過符號 & 組合在一塊兒。若是咱們只想要表格列中條件的子集條件,那麼能夠經過添加另一對方括號來表示。
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']] |
默認狀況下,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) |
咱們知道了如何對值進行篩選,但如何對一個列表進行篩選呢,如同 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'])] |
分組操做很簡單:使用 .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()
,咱們從新進行數據幀的行編號。
在 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) |
假設咱們作了一些初步查詢,如今有一個名爲 by_country 的 dataframe,它包含每一個國家的機場數量:
在接下來的第一個示例中,咱們經過 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) |
如今給定一組 dataframe,或者一組跑道數據:
計算跑道長度的最小值,最大值,平均值和中值:
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 的彙集方法,每一個統計結果都是一行數據:
不用擔憂 — 只需將 dataframe 經過 .T 進行轉換就能夠獲得成列的數據:
使用 .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']] |
使用 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()。
到目前爲止,咱們一直在講篩選,可是在您的探索性分析過程當中,您可能也須要修改。若是您想添加一些遺漏的記錄你該怎麼辦?
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) |
如今咱們須要修改原始 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' |
從 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) |
我須要說起一件重要的事情 — 不可變性。默認狀況下,大部分應用於 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 查詢結果、繪圖和相關內容的最佳媒介是 Jupyter notebooks(jupyter.org/)。事實上,有些人(好比傑克·範德普拉斯(Jake Vanderplas),他太棒了)會把整本書都發布在 Jupyter notebooks 上:github.com/jakevdp/Pyt…。
很簡單就能夠建立一個新的筆記本:
pip install jupyter
jupyter notebook
複製代碼
以後:
GitHub 有一個很棒的內置查看器,能夠以 Markdown 的格式顯示 Jupyter notebooks 的內容。
我但願您如今確信,Pandas 庫能夠像您的老朋友 SQL 同樣幫助您進行探索性數據分析,在某些狀況下甚至會作得更好。是時候你本身動手開始在 Pandas 裏查詢數據了!
✉️ 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 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。