作為 pandas 教程的第四篇,本篇将對比 sql 語言,學習 pandas 中各種類 sql 操作,文章篇幅較長
為了友善,依然以下面這個 DataFrame 為例,其變量名為 df,設有一同樣結構的 SQL 表,表名為 tb:
需求:選擇成績大于 90 分的男生的成績單
sql 寫法:select * from tb where sex="male" and grade>90
pandas 寫法:and 符号 &,df[(df['sex']=='male') & (df['grade']>90)]
常見的 pandas 錯誤寫法:
由于 sql 的思維慣性,把 & 寫成 and。
& 兩側沒加括号,寫成df[df['sex']=='male' & df['grade']>90],這時會報錯:TypeError: cannot compare a dtyped [int64] array with a scalar of type [bool],從字面意思來看是 int64 數組和布爾值無法比較,真正的原因是因為 ==、> 運算符的優先級并不比 & 高,從左往右看,第一個運算 df['sex']=='male'的結果就是一個布爾值,然後這個布爾值再與 df['grade'] 作 & 運算,這樣就報錯了。解決辦法就是像正确寫法那樣,& 兩側加括号。
這樣選擇出來的 dataframe,其 index 是不連續的,因為 pandas 的選擇,連同原來的 index 一起選擇了,符合條件的行,在原來的 dataframe 中,index 幾乎不可能連續(除開巧合)。是以必須 reset_index 下,這個函數有兩個值得注意的參數 inplace 和 drop,inplace 在 強大的 DataFrame 就講過,如果原地修改就設定為 True;至于這個 drop ,設定為 False 則 index 列會被還原為普通列,否則的話就直接丢失,這裡我們設定為 True,直接丢掉,否則的話,就會出現以隻帶檔案名方式讀取了儲存 index 的 csv 檔案那樣的錯誤:出現一列 "unnamed: 0"。and_df = df[(df['sex']=='male') & (df['grade']>90)]
and_df.reset_index(inplace=True, drop=True)
需求:選擇分數大于 95 或者小于 85 的學生的成績單sql 寫法:select * from tb where grade>95 or grade<85pandas 寫法:or 符号 |,即df[(df['grade']>95) | (df['grade']<85)]
需求:選擇分數大于等于 85 并且小于等于 95 分的學生分析:這個需求有很多寫法,可以用 and, 也可以利用求補思想,即 notsql 寫法:select * frim tb where grade not in(select grade from tb where grade>85 and grade<95)pandas 寫法:not 符号 -,即 df[-((df['grade']>95) | (df['grade']<85))]
需求:選擇分數為 89、95 之一的學生sql 寫法:select * from tb where grade in (89, 95)pandas 寫法:df[df['grade'].isin([89, 95])]上述的四個例子,都是整行查詢,如果隻需要查詢某個字段,比如查詢男生中國文成績最差的學生的名字,以通過 loc 表達式實作,如下:math_best_student = df.loc[(df['course']=='chinese')&(df['sex']=='male'), ['name','grade']].sort_values('grade').reset_index(drop=True).iloc[0, 0]這行代碼文法糖較多,分三步拆解:
條件選擇:所有男生的國文成績的姓名、(國文)成績兩個字段
成績升序:按照成績升序排列,注意 reset_index 重置索引,不需要 inplace,否則沒有傳回值,無法再黏文法糖。
切片選擇:第 0 個學生,即成績最差的學生的第 0 列 ,即 name 列。
groupby 即分組聚合,df.group_by() 即可實作,它傳回的是一個 GroupBy 對象而不是 dataframe
需要對這個 GroupBy 對象進行後續的聚合函數調用才會傳回 dataframe。需求:數學、國文、英語三門課各自的平均分,最高分、最低分sql 寫法:select avg(grade),max(grade),min(grade) from tb group by coursepandas 寫法:gb_df = df.groupby('course').agg({
'grade': ['mean', 'max', 'min']
})其列印結果如下:
sql 中有四種連接配接:内連接配接,左外連接配接,右外連接配接,全外連接配接,
以 df 為左表,right_df 為右表,在 name 字段連接配接為例。df = pd.DataFrame({'name':['Bob','Alice','Joe']*3,
'sex':['male','female','male']*3,
'course': ['math', 'english', 'chinese', 'chinese', 'chinese', 'english', 'english', 'math', 'math'],
'grade':[99,92,89,88,95,93,95,79,89]})
right_df = pd.DataFrame({
'name': ['Bob', 'Alice', 'Miller'],
'number': [1001, 1002, 1003]
})
連接配接
解釋
内連接配接
在 name 列上取交集,隻保留左右兩表都出現的 name,即隻有 Bob、Alice 兩人的共六門成績
左外連接配接
保留左表中 name 中出現的而右表沒有出現的,同時對應右表的 number 字段置空
右外連接配接
參見左外連接配接
全外連接配接
都置空
pandas 有 merge 和 join 兩個函數可以實作連接配接,差別如下:
merge 預設在左右兩表中相同列合并,也可以 on, left_on, right_on 指定(左/右)列名
join 預設在 index 列合并,也可以 on 指定,沒有 left_on、right_on
可以看到 merge 使用範圍更廣一點。這四種連接配接對應的 sql 及 pandas 寫法如下表:
sql
pandas
select * from tb inner join right_tb on tb.name=right_tb.name
df.merge(right_df, how='inner') df.merge(right_df, on='name', how='inner')
df.merge(right_df, left_on='name', right_on='name', how='inner')
select * from tb left join right_tb on tb.name=right_tb.name
df.merge(right_df, how='left')
select * from tb right join right_tb on tb.name=right_tb.name
df.merge(right_df, how='right')
select * from tb full outer join right_tb on tb.name=right_tb.name
df.merge(right_df, how='outer')