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

15 年前,軟件開發人員只需掌握不多的一些技能,他或她就有機會得到 95% 的工做機會。這些技能包括:html

  • 面向對象編程
  • 腳本語言
  • JavaScript 以及其餘
  • SQL

當您須要快速瀏覽一些數據並得出初步結論時,SQL 是一種經常使用的工具,這些結論可能會產生一個分析報告或者是編寫一個應用程序。這被稱之爲 探索性分析前端

現現在,數據會以各類各樣的形式出現,再也不僅僅是「關係型數據庫」的同義詞。您的數據可能會是 CSV 文件、純文本、Parquet、HDF5,或者其餘什麼格式。這些正是 Pandas 庫的亮點所在。android

什麼是 Pandas?

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 查詢

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,它包含每一個國家的機場數量:

在接下來的第一個示例中,咱們經過 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,或者一組跑道數據:

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

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 進行轉換就能夠獲得成列的數據:

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 查詢結果、繪圖和相關內容的最佳媒介是 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 裏查詢數據了!

✉️ 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前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索