可以用 pandas dataframe 这样写 SQL
Posted on Tue 02 May 2023 in Journal
Abstract | 可以用 pandas dataframe 这样写 SQL |
---|---|
Authors | Walter Fan |
Category | learning note |
Status | v1.0 |
Updated | 2023-05-02 |
License | CC-BY-NC-ND 4.0 |
过去若干年, 数据分析的第一步是将数据解析并存储到数据库表中, 然后用 SQL 来分析. 现在我用的更多的是用 python 将数据读入内存, 保存到 Pandas DataFrame 中, 用 Pandas 来分析.
其实 Pandas 所用的基本方法和 SQL 是差不多的, Pandas 有篇对比 Pandas 和 SQL 的文章 https://pandas.pydata.org/docs/getting_started/comparison/comparison_with_sql.html 写的很好
我随手在 jupyter notebook 写下了相关的例子, 代码如下:
"""
refer to https://pandas.pydata.org/docs/getting_started/comparison/comparison_with_sql.html
"""
import pandas as pd
import numpy as np
url = (
"https://raw.githubusercontent.com/pandas-dev"
"/pandas/main/pandas/tests/io/data/csv/tips.csv"
)
sqls = [
"SELECT total_bill, tip, smoker, time FROM tips",
"SELECT *, tip/total_bill as tip_rate FROM tips",
"SELECT * FROM tips WHERE total_bill > 10 and time = 'Dinner'",
"SELECT * FROM tips WHERE size >= 5 OR total_bill > 45",
"SELECT sex, count(*) FROM tips GROUP BY sex",
"SELECT day, AVG(tip), COUNT(*) FROM tips GROUP BY day",
"SELECT smoker, day, COUNT(*), AVG(tip) FROM tips GROUP BY smoker, day",
"SELECT * FROM df1 INNER JOIN df2 ON df1.key = df2.key",
"SELECT * FROM df1 LEFT OUTER JOIN df2 ON df1.key = df2.key",
"SELECT * FROM df1 RIGHT OUTER JOIN df2 ON df1.key = df2.key",
"SELECT * FROM df1 FULL OUTER JOIN df2 ON df1.key = df2.key",
"SELECT city, rank FROM df1 UNION ALL SELECT city, rank FROM df2",
"SELECT * FROM tips ORDER BY tip DESC LIMIT 10 OFFSET 5",
"DELETE FROM tips WHERE tip > 9"
]
cmds = [
'tips[["total_bill", "tip", "smoker", "time"]]',
'tips.assign(tip_rate=tips["tip"] / tips["total_bill"])',
'tips[(tips["time"] == "Dinner") & (tips["tip"] > 5.00)]',
'tips[(tips["size"] >= 5) | (tips["total_bill"] > 45)]',
'tips.groupby("sex").size()',
'tips.groupby("day").agg({"tip": np.mean, "day": np.size})',
'tips.groupby(["smoker", "day"]).agg({"tip": [np.size, np.mean]})',
'pd.merge(df1, df2, on="key")',
'pd.merge(df1, df2, on="key", how="left")',
'pd.merge(df1, df2, on="key", how="right")',
'pd.merge(df1, df2, on="key", how="outer")',
'pd.concat([df1, df2]).drop_duplicates()',
'tips.nlargest(10 + 5, columns="tip").tail(10)',
'tips.loc[tips["tip"] <= 9]'
]
seps = "-"*60
df1 = pd.DataFrame({"key": ["A", "B", "C", "D"], "value": np.random.randn(4)})
df2 = pd.DataFrame({"key": ["B", "D", "D", "E"], "value": np.random.randn(4)})
df3 = pd.DataFrame(
{"city": ["Chicago", "San Francisco", "New York City"], "rank": range(1, 4)}
)
df4 = pd.DataFrame(
{"city": ["Chicago", "Boston", "Los Angeles"], "rank": [1, 4, 5]}
)
tips = pd.read_csv(url)
print(f"DataFrame\n{seps}")
print(tips)
for i in range(0, len(sqls)):
print(f"\n{i}. {sqls[i]} \n{seps}\n {cmds[i]} \n{seps}")
eval(f"print({cmds[i]})")
输出如下
DataFrame
------------------------------------------------------------
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
.. ... ... ... ... ... ... ...
239 29.03 5.92 Male No Sat Dinner 3
240 27.18 2.00 Female Yes Sat Dinner 2
241 22.67 2.00 Male Yes Sat Dinner 2
242 17.82 1.75 Male No Sat Dinner 2
243 18.78 3.00 Female No Thur Dinner 2
[244 rows x 7 columns]
0. SELECT total_bill, tip, smoker, time FROM tips
------------------------------------------------------------
tips[["total_bill", "tip", "smoker", "time"]]
------------------------------------------------------------
total_bill tip smoker time
0 16.99 1.01 No Dinner
1 10.34 1.66 No Dinner
2 21.01 3.50 No Dinner
3 23.68 3.31 No Dinner
4 24.59 3.61 No Dinner
.. ... ... ... ...
239 29.03 5.92 No Dinner
240 27.18 2.00 Yes Dinner
241 22.67 2.00 Yes Dinner
242 17.82 1.75 No Dinner
243 18.78 3.00 No Dinner
[244 rows x 4 columns]
1. SELECT *, tip/total_bill as tip_rate FROM tips
------------------------------------------------------------
tips.assign(tip_rate=tips["tip"] / tips["total_bill"])
------------------------------------------------------------
total_bill tip sex smoker day time size tip_rate
0 16.99 1.01 Female No Sun Dinner 2 0.059447
1 10.34 1.66 Male No Sun Dinner 3 0.160542
2 21.01 3.50 Male No Sun Dinner 3 0.166587
3 23.68 3.31 Male No Sun Dinner 2 0.139780
4 24.59 3.61 Female No Sun Dinner 4 0.146808
.. ... ... ... ... ... ... ... ...
239 29.03 5.92 Male No Sat Dinner 3 0.203927
240 27.18 2.00 Female Yes Sat Dinner 2 0.073584
241 22.67 2.00 Male Yes Sat Dinner 2 0.088222
242 17.82 1.75 Male No Sat Dinner 2 0.098204
243 18.78 3.00 Female No Thur Dinner 2 0.159744
[244 rows x 8 columns]
2. SELECT * FROM tips WHERE total_bill > 10 and time = 'Dinner'
------------------------------------------------------------
tips[(tips["time"] == "Dinner") & (tips["tip"] > 5.00)]
------------------------------------------------------------
total_bill tip sex smoker day time size
23 39.42 7.58 Male No Sat Dinner 4
44 30.40 5.60 Male No Sun Dinner 4
47 32.40 6.00 Male No Sun Dinner 4
52 34.81 5.20 Female No Sun Dinner 4
59 48.27 6.73 Male No Sat Dinner 4
116 29.93 5.07 Male No Sun Dinner 4
155 29.85 5.14 Female No Sun Dinner 5
170 50.81 10.00 Male Yes Sat Dinner 3
172 7.25 5.15 Male Yes Sun Dinner 2
181 23.33 5.65 Male Yes Sun Dinner 2
183 23.17 6.50 Male Yes Sun Dinner 4
211 25.89 5.16 Male Yes Sat Dinner 4
212 48.33 9.00 Male No Sat Dinner 4
214 28.17 6.50 Female Yes Sat Dinner 3
239 29.03 5.92 Male No Sat Dinner 3
3. SELECT * FROM tips WHERE size >= 5 OR total_bill > 45
------------------------------------------------------------
tips[(tips["size"] >= 5) | (tips["total_bill"] > 45)]
------------------------------------------------------------
total_bill tip sex smoker day time size
59 48.27 6.73 Male No Sat Dinner 4
125 29.80 4.20 Female No Thur Lunch 6
141 34.30 6.70 Male No Thur Lunch 6
142 41.19 5.00 Male No Thur Lunch 5
143 27.05 5.00 Female No Thur Lunch 6
155 29.85 5.14 Female No Sun Dinner 5
156 48.17 5.00 Male No Sun Dinner 6
170 50.81 10.00 Male Yes Sat Dinner 3
182 45.35 3.50 Male Yes Sun Dinner 3
185 20.69 5.00 Male No Sun Dinner 5
187 30.46 2.00 Male Yes Sun Dinner 5
212 48.33 9.00 Male No Sat Dinner 4
216 28.15 3.00 Male Yes Sat Dinner 5
4. SELECT sex, count(*) FROM tips GROUP BY sex
------------------------------------------------------------
tips.groupby("sex").size()
------------------------------------------------------------
sex
Female 87
Male 157
dtype: int64
5. SELECT day, AVG(tip), COUNT(*) FROM tips GROUP BY day
------------------------------------------------------------
tips.groupby("day").agg({"tip": np.mean, "day": np.size})
------------------------------------------------------------
tip day
day
Fri 2.734737 19
Sat 2.993103 87
Sun 3.255132 76
Thur 2.771452 62
6. SELECT smoker, day, COUNT(*), AVG(tip) FROM tips GROUP BY smoker, day
------------------------------------------------------------
tips.groupby(["smoker", "day"]).agg({"tip": [np.size, np.mean]})
------------------------------------------------------------
tip
size mean
smoker day
No Fri 4 2.812500
Sat 45 3.102889
Sun 57 3.167895
Thur 45 2.673778
Yes Fri 15 2.714000
Sat 42 2.875476
Sun 19 3.516842
Thur 17 3.030000
7. SELECT * FROM df1 INNER JOIN df2 ON df1.key = df2.key
------------------------------------------------------------
pd.merge(df1, df2, on="key")
------------------------------------------------------------
key value_x value_y
0 B 0.421341 -0.306186
1 D -0.213784 0.187269
2 D -0.213784 -1.267491
8. SELECT * FROM df1 LEFT OUTER JOIN df2 ON df1.key = df2.key
------------------------------------------------------------
pd.merge(df1, df2, on="key", how="left")
------------------------------------------------------------
key value_x value_y
0 A -0.274974 NaN
1 B 0.421341 -0.306186
2 C 0.473855 NaN
3 D -0.213784 0.187269
4 D -0.213784 -1.267491
9. SELECT * FROM df1 RIGHT OUTER JOIN df2 ON df1.key = df2.key
------------------------------------------------------------
pd.merge(df1, df2, on="key", how="right")
------------------------------------------------------------
key value_x value_y
0 B 0.421341 -0.306186
1 D -0.213784 0.187269
2 D -0.213784 -1.267491
3 E NaN -0.191261
10. SELECT * FROM df1 FULL OUTER JOIN df2 ON df1.key = df2.key
------------------------------------------------------------
pd.merge(df1, df2, on="key", how="outer")
------------------------------------------------------------
key value_x value_y
0 A -0.274974 NaN
1 B 0.421341 -0.306186
2 C 0.473855 NaN
3 D -0.213784 0.187269
4 D -0.213784 -1.267491
5 E NaN -0.191261
11. SELECT city, rank FROM df1 UNION ALL SELECT city, rank FROM df2
------------------------------------------------------------
pd.concat([df1, df2]).drop_duplicates()
------------------------------------------------------------
key value
0 A -0.274974
1 B 0.421341
2 C 0.473855
3 D -0.213784
0 B -0.306186
1 D 0.187269
2 D -1.267491
3 E -0.191261
12. SELECT * FROM tips ORDER BY tip DESC LIMIT 10 OFFSET 5
------------------------------------------------------------
tips.nlargest(10 + 5, columns="tip").tail(10)
------------------------------------------------------------
total_bill tip sex smoker day time size
183 23.17 6.50 Male Yes Sun Dinner 4
214 28.17 6.50 Female Yes Sat Dinner 3
47 32.40 6.00 Male No Sun Dinner 4
239 29.03 5.92 Male No Sat Dinner 3
88 24.71 5.85 Male No Thur Lunch 2
181 23.33 5.65 Male Yes Sun Dinner 2
44 30.40 5.60 Male No Sun Dinner 4
52 34.81 5.20 Female No Sun Dinner 4
85 34.83 5.17 Female No Thur Lunch 4
211 25.89 5.16 Male Yes Sat Dinner 4
13. DELETE FROM tips WHERE tip > 9
------------------------------------------------------------
tips.loc[tips["tip"] <= 9]
------------------------------------------------------------
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
.. ... ... ... ... ... ... ...
239 29.03 5.92 Male No Sat Dinner 3
240 27.18 2.00 Female Yes Sat Dinner 2
241 22.67 2.00 Male Yes Sat Dinner 2
242 17.82 1.75 Male No Sat Dinner 2
243 18.78 3.00 Female No Thur Dinner 2
[243 rows x 7 columns]
1
Reference
- https://deepnote.com/blog/query-pandas-dataframes-with-sql
- https://towardsdatascience.com/query-pandas-dataframe-with-sql-2bb7a509793d
- https://pandas.pydata.org/docs/getting_started/comparison/comparison_with_sql.html
- https://towardsdatascience.com/pandas-equivalent-of-10-useful-sql-queries-f79428e60bd9
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。