バッチ処理などで一時テーブルにCSVを投入してデータを処理してましたが
Pandasを使ってみたら高速で尚且つSELECT文を扱うようにCSVの操作が
出来たのでまとめて見ました。
Pandasでデータ分析と聞くと難しそうで距離をおいてましたが
大量データの処理はPandasでいこうかと思っています
Excelで大量のデータを扱ってる人にもExcelを直接開くことが出来るのでお勧めです
- 前準備
- CSV読み込み
- SELECT
- OREDER BY
- WHERE
- MAX, MIN, AVG, SUM, COUNT
- GROUP BY
- JOIN
- AS
- OFFSET FETCH LIMIT
- INSERT
- UPDATE
- DELETE
前準備
- pandas
データ解析機能を提供するpythonライブラリ
pipでpandasをインストール
pip install pandas
# または pip3 install pandas
今回使用するCSVファイルがこちらになります
name,salary,postcode, birthday
山田, 200, 038-0056, 2004/1/3
佐藤, 300, 300-0727, 1999/6/30
鈴木, 500, 132-0013, 1980/3/3
川田, 700, 300-0727, 1975/11/23
山田, 250, 038-0056, 2004/1/25
山田, 350, 030-0932, 1987/9/12
name,address
山田, 青森
田中, 秋田
佐藤, 茨城
鈴木, 東京
name,salary,age
山田, 200, 20
山田, 350, 44
佐藤, 300, 25
鈴木, 500, 37
※実際に使う場合は空白の部分を詰めてください
CSV読み込み
import pandas as pd
df_a = pd.read_csv("./hoge_a.csv")
df_b = pd.read_csv("./hoge_b.csv")
read_csvでCSVをDataFrameオブジェクトとして読み込んでいます
パラメータを指定していないので、一行目が列名になります
pd.read_csv("./hoge_a.csv",names=['a','b','c','d'])
のようにすればnamesの部分を列名にすることもできます
(その場合には、一行目もデータとして扱われます)
read_csvのパラメータ一覧
SELECT
# SELECT name, salary FROM df_a
print(df_a[["name", "salary"]])
実行結果
name salary
0 山田 200
1 佐藤 300
2 鈴木 500
3 川田 700
4 山田 250
5 山田 350
DataFrameから指定した列名の列を選択しています
["列名1", "列名2"]
のように複数の列名をリストで渡します
ORDER BY
# SELECT * FROM df_a ORDER BY name DESC, salary DESC
print(df_a.sort_values(by=["name", "salary"], ascending=False, ignore_index=True))
実行結果
name salary postcode birthday
0 鈴木 500 132-0013 1980/3/3
1 川田 700 300-0727 1975/11/23
2 山田 350 030-0932 1987/9/12
3 山田 250 038-0056 2004/1/25
4 山田 200 038-0056 2004/1/3
5 佐藤 300 300-0727 1999/6/30
sort_values
で渡した列名のリストをターゲットにして並び替えています(リストの最初に指定した列が優先されます)
使用しているパラメータ
- by(必須):並び替えの対象にする列名
- ascending:昇順かどうか(Falseで降順)
-
ignore_index:並び順を変更する前のindexを無視するかどうか
(Trueでindexを新しく振りなおします)
日付はCSVから読み込んだ段階では、str
なので(例:3と25が正しく並び変わらない)ORDER BY
する際には、日付型に変更する必要があります
# SELECT * FROM df_a ORDER BY birthday DESC
df_a["birthday"] = pd.to_datetime(df_a["birthday"])
print(df_a.sort_values(by=["birthday"], ascending=False, ignore_index=True))
実行結果
変換なし
name salary postcode birthday
0 山田 200 038-0056 2004/1/3
1 山田 250 038-0056 2004/1/25
2 佐藤 300 300-0727 1999/6/30
3 山田 350 030-0932 1987/9/12
4 鈴木 500 132-0013 1980/3/3
5 川田 700 300-0727 1975/11/23
変換あり
name salary postcode birthday
0 山田 250 038-0056 2004-01-25
1 山田 200 038-0056 2004-01-03
2 佐藤 300 300-0727 1999-06-30
3 山田 350 030-0932 1987-09-12
4 鈴木 500 132-0013 1980-03-03
5 川田 700 300-0727 1975-11-23
WHERE
# SELECT * FROM df_a WHERE salary >= 400 AND postcode == "300-0727"
print(df_a[(df_a["salary"] >= 400) & (df_a["postcode"] == "300-0727")])
# または
print(df_a.query("salary >= 400 and postcode == '300-0727'"))
実行結果
name salary postcode birthday
3 川田 700 300-0727 1975/11/23
df_a[条件]で条件にあった行を抽出できます
またはquery
に条件式を渡しても同じように行を抽出できます
df_a[(df_a["salary"] >= 400) & (df_a["postcode"] == "300-0727")]
やdf_a.query("salary >= 400 and postcode == '300-0727'")
もDataFrameになるので
df[["salary", "name"]]
のように列を抽出できます
# SELECT salary, name FROM df_a WHERE salary == 1 AND postcode == 'a'
df_new = df_a[(df_a["salary"] >= 400) & (df_a["postcode"] == "300-0727")]
# または
df_new = df_a.query("salary >= 400 and postcode == '300-0727'")
print(df_new[["salary", "name"]])
実行結果
salary name
3 700 川田
MAX, MIN, AVG, SUM, COUNT
# MAX
# SELECT MAX(name), MAX(salary), MAX(postcode), MAX(birthday) FROM df_a
df_a["birthday"] = pd.to_datetime(df_a["birthday"]) # 日付は変換が必要
print(df_a[["name", "salary", "postcode", "birthday"]].max())
# MIN
# SELECT MIN(name), MIN(salary), MIN(postcode), MIN(birthday) FROM df_a
df_a["birthday"] = pd.to_datetime(df_a["birthday"]) # 日付は変換が必要
print(df_a[["name", "salary", "postcode", "birthday"]].min())
# AVG
# SELECT AVG(salary) FROM df_a
print(df_a["salary"].mean())
# SUM
# SELECT SUM(name), SUM(salary) FROM df_a
print(df_a[["name", "salary"]].sum())
# COUNT
# SELECT COUNT(name) FROM df_a
print(df_a[["name"]].count())
実行結果
# MAX
name 鈴木
salary 700
postcode 300-0727
birthday 2004-01-25 00:00:00
dtype: object
# MIN
name 佐藤
salary 200
postcode 030-0932
birthday 1975-11-23 00:00:00
dtype: object
# AVG
383.3333333333333
# SUM
name 山田佐藤鈴木川田山田山田
salary 2300
dtype: object
# COUNT
name 6
dtype: int64
df_a[["列名1", "列名2"]]
で列を指定してmax(最大値)
min(最小値)
mean(平均値)
sum(合計値)
count(個数)
をそれぞれ実行しています
maxのパラメータ
minのパラメータ
meanのパラメータ
sumのパラメータ
countのパラメータ
GROUP BY
# SELECT name, postcode, SUM(salary) FROM df_a GROUP BY name, postcode
print(df_a.groupby(["name", "postcode"])["salary"].sum().reset_index())
実行結果
name postcode salary
0 佐藤 300-0727 300
1 山田 030-0932 350
2 山田 038-0056 450
3 川田 300-0727 700
4 鈴木 132-0013 500
groupby(列名)
でグループ化する列を指定してその後の[列名]
をsum(合計)
しています。また、reset_index
で新しくインデックスを振りなおしています
groupbyのパラメータ
reset_indexのパラメータ
JOIN
# SELECT * FROM df_a LEFT JOIN df_b ON df_a.name = df_b.name
print(pd.merge(df_a, df_b, on="name", how="left"))
実行結果
name salary postcode birthday address
0 山田 200 038-0056 2004/1/3 青森
1 佐藤 300 300-0727 1999/6/30 茨城
2 鈴木 500 132-0013 1980/3/3 東京
3 川田 700 300-0727 1975/11/23 NaN
4 山田 250 038-0056 2004/1/25 青森
5 山田 350 030-0932 1987/9/12 青森
merge
を使って二つのテーブルを結合しています
使用しているパラメータ
- on:結合の対象にする列名(配列で渡せば複数列も可能)
-
how:結合方法の指定(defaultでは
inner
)
pd.merge(df_a, df_b, on="name", how="left")
これもDataFrameになりますので、さらに結合できます。
# SELECT * FROM df_a LEFT JOIN df_b ON df_a.name = df_b.name LEFT JOIN df_a.name = df_c.name
df_ab = pd.merge(df_a, df_b, on="name", how="left")
df_abc = pd.merge(df_ab, df_c, on=["name", "salary"], how="left")
df_abc["age"] = df_abc["age"].astype(pd.Int64Dtype())
print(df_abc)
実行結果
name salary postcode birthday address age
0 山田 200 038-0056 2004/1/3 青森 20
1 佐藤 300 300-0727 1999/6/30 茨城 25
2 鈴木 500 132-0013 1980/3/3 東京 37
3 川田 700 300-0727 1975/11/23 NaN <NA>
4 山田 250 038-0056 2004/1/25 青森 <NA>
5 山田 350 030-0932 1987/9/12 青森 44
df_abc["age"] = df_abc["age"].astype(pd.Int64Dtype())
では結合でint
の列にNaNがあるとfloat
になるのNaNを扱える整数型Int64
で変換
AS
# SELECT name, postcode, name + postcode AS name_postcode FROM df_a
df_a["name_postcode"] = df_a["name"] + df_a["postcode"] # 新しい列を追加
print(df_a[["name", "postcode", "name_postcode"]])
# SELECT name, salary, name + salary AS name_salary FROM df_a
df_a["name_salary"] = df_a["name"] + df_a["salary"].astype(str) # 型が違う場合は.astypeで変換する
print(df_a[["name", "salary", "name_salary"]])
実行結果
name postcode name_postcode
0 山田 038-0056 山田038-0056
1 佐藤 300-0727 佐藤300-0727
2 鈴木 132-0013 鈴木132-0013
3 川田 300-0727 川田300-0727
4 山田 038-0056 山田038-0056
5 山田 030-0932 山田030-0932
name salary name_salary
0 山田 200 山田200
1 佐藤 300 佐藤300
2 鈴木 500 鈴木500
3 川田 700 川田700
4 山田 250 山田250
5 山田 350 山田350
AS句で表示したい列を新しくDataFrameに追加しています
OFFSET FETCH LIMIT
# SELECT * FROM df_a ORDER BY 何かしらの列名 OFFSET 2 ROWS FETCH NEXT 3 ROWS ONLY
# SELECT * FROM df_a LIMIT 3 OFFSET 2
print(df_a.loc[2:4])
実行結果
name salary postcode birthday
2 鈴木 500 132-0013 1980/3/3
3 川田 700 300-0727 1975/11/23
4 山田 250 038-0056 2004/1/25
loc[インデックス始まり:インデックス終わり]
で指定の行から指定の行数を取得できます
INSERT
# INSERT INTO df_a (name, salary, postcode, birthday) VALUES ("新田", 280, "682-0022", "2000/12/10")
df_insert = pd.DataFrame({"name":["新田"], "salary":[280], "postcode":["682-0022"], "birthday":["2000/12/10"] })
df_a = pd.concat([df_a, df_insert])
print(df_a.reset_index(drop=True))
実行結果
name salary postcode birthday
0 山田 200 038-0056 2004/1/3
1 佐藤 300 300-0727 1999/6/30
2 鈴木 500 132-0013 1980/3/3
3 川田 700 300-0727 1975/11/23
4 山田 250 038-0056 2004/1/25
5 山田 350 030-0932 1987/9/12
6 新田 280 682-0022 2000/12/10
追加する行のDataFrameを新しく作り(df_insert
)それを元のDataFrame(df_a
)に足しています。また、reset_index
で新しくインデックスを振りなおしています
使用しているパラメータ
- drop:古いインデックスを削除するかどうかの指定
concatのパラメータ
reset_indexのパラメータ
UPDATE
# UPDATE df_a SET name = "上田" WHERE salary >= 250
df_a.loc[(df_a["salary"]>=500),["name"]]="上田"
print(df_a)
実行結果
name salary postcode birthday
0 山田 200 038-0056 2004/1/3
1 佐藤 300 300-0727 1999/6/30
2 上田 500 132-0013 1980/3/3
3 上田 700 300-0727 1975/11/23
4 山田 250 038-0056 2004/1/25
5 山田 350 030-0932 1987/9/12
loc[(条件式),[列名]]
を使ってdf_a
の指定の値を上書きしています
DELETE
# DELETE FROM df_a WHERE name = 山田
print(df_a[df_a["name"]!="山田"])
実行結果
name salary postcode birthday
1 佐藤 300 300-0727 1999/6/30
2 鈴木 500 132-0013 1980/3/3
3 川田 700 300-0727 1975/11/23
name=山田
を削除する
↓ 言い換えれば
name!=山田
を表示する
となりますので↑のコードではname!=山田
の行だけを表示しています
参考にさせていただいた記事、サイト