[2023-10-13] reverse=True
は descending=True
に変更されていましたので修正(Thanks: Nobu C. Shirai先生)。
Polars(ポーラーズ)はRust・Python用のデータ操作ライブラリです。Pythonではpandasを置き換えるものです。日本語の解説として超高速…だけじゃない!Pandasに代えてPolarsを使いたい理由が参考になります。
Polarsのマスコットは白熊(ホッキョクグマ、polar bear)です。パンダより強そうです。実際、pandasより高速で、機能も豊富です。
インストールは pip install polars
です。
例(フォントによっては表が崩れます):
import polars as pl df = pl.read_csv("https://okumuralab.org/~okumura/stat/data/pop2022.csv") df
shape: (47, 4) ┌──────┬────────────┬─────────┬─────────┐ │ 番号 ┆ 都道府県 ┆ 男 ┆ 女 │ │ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ str ┆ i64 ┆ i64 │ ╞══════╪════════════╪═════════╪═════════╡ │ 1 ┆ 北海道 ┆ 2450393 ┆ 2733294 │ │ 2 ┆ 青森 ┆ 589143 ┆ 653938 │ │ 3 ┆ 岩手 ┆ 581809 ┆ 624670 │ │ 4 ┆ 宮城 ┆ 1106183 ┆ 1162172 │ │ ... ┆ ... ┆ ... ┆ ... │ │ 44 ┆ 大分 ┆ 538934 ┆ 592206 │ │ 45 ┆ 宮崎 ┆ 511039 ┆ 567274 │ │ 46 ┆ 鹿児島 ┆ 759364 ┆ 846055 │ │ 47 ┆ 沖縄 ┆ 732981 ┆ 752689 │ └──────┴────────────┴─────────┴─────────┘
1列目の都道府県番号は01〜47の文字列として読みたいですね:
df = pl.read_csv("https://okumuralab.org/~okumura/stat/data/pop2022.csv", dtypes=[pl.Utf8]) df
shape: (47, 4) ┌──────┬────────────┬─────────┬─────────┐ │ 番号 ┆ 都道府県 ┆ 男 ┆ 女 │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ i64 ┆ i64 │ ╞══════╪════════════╪═════════╪═════════╡ │ 01 ┆ 北海道 ┆ 2450393 ┆ 2733294 │ │ 02 ┆ 青森 ┆ 589143 ┆ 653938 │ │ 03 ┆ 岩手 ┆ 581809 ┆ 624670 │ │ 04 ┆ 宮城 ┆ 1106183 ┆ 1162172 │ │ ... ┆ ... ┆ ... ┆ ... │ │ 44 ┆ 大分 ┆ 538934 ┆ 592206 │ │ 45 ┆ 宮崎 ┆ 511039 ┆ 567274 │ │ 46 ┆ 鹿児島 ┆ 759364 ┆ 846055 │ │ 47 ┆ 沖縄 ┆ 732981 ┆ 752689 │ └──────┴────────────┴─────────┴─────────┘
dtypes
はリストで例えば dtypes=[pl.Utf8, pl.Utf8, pl.Int32, pl.Int32]
のように列ごとに指定できます。4列全部を文字列にするには dtypes=[pl.Utf8]*4
とします。
型は pl.Utf8
(文字列)、pl.Float64
、pl.Float32
、pl.Int64
、pl.Int32
、pl.Int16
、pl.Int8
、pl.UInt64
、pl.UInt32
、pl.UInt16
、pl.UInt8
などが使えます。
Pandasと比べて、0から始まる行インデックスが付きません。
別の例:
URL = "https://www.data.jma.go.jp/cpdinfo/temp/list/csv/an_wld.csv" df1 = pl.read_csv(URL, encoding="cp932")
これは悪名高き気象庁のCSVファイルで、ときどき数値の後に *
が付いてエラーになります。pandasはエラーになったら面倒ですが、Polarsなら次のように回避できます:
df1 = pl.read_csv(URL, encoding="cp932", ignore_errors=True) df1
shape: (132, 4) ┌──────┬────────────┬─────────┬─────────┐ │ 年 ┆ 世界全体 ┆ 北半球 ┆ 南半球 │ │ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ f64 ┆ f64 ┆ f64 │ ╞══════╪════════════╪═════════╪═════════╡ │ 1891 ┆ -0.78 ┆ -0.88 ┆ -0.68 │ │ 1892 ┆ -0.89 ┆ -1.0 ┆ -0.74 │ │ 1893 ┆ -0.94 ┆ -1.06 ┆ -0.79 │ │ 1894 ┆ -0.86 ┆ -0.93 ┆ -0.77 │ │ ... ┆ ... ┆ ... ┆ ... │ │ 2019 ┆ 0.31 ┆ 0.38 ┆ 0.23 │ │ 2020 ┆ 0.34 ┆ 0.51 ┆ 0.16 │ │ 2021 ┆ 0.22 ┆ 0.35 ┆ 0.09 │ │ 2022 ┆ null ┆ null ┆ null │ └──────┴────────────┴─────────┴─────────┘
得られた df
の扱い方は、pandasとやや異なります。例:
df.query("都道府県 == '東京'") # pandas df.filter(pl.col("都道府県") == "東京") # Polars
df.to_pandas()
や df.to_numpy()
でpandasやnumpyに変換することもできます。
pandasによるクエリと同じことをPolarsで行ってみましょう。使うのは、さきほど
df = pl.read_csv("https://okumuralab.org/~okumura/stat/data/pop2022.csv", dtypes=[pl.Utf8])
で読み込んだデータです。まずは東京都だけ選びます:
df.filter(pl.col("都道府県") == "東京")
shape: (1, 4) ┌──────┬────────────┬─────────┬─────────┐ │ 番号 ┆ 都道府県 ┆ 男 ┆ 女 │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ i64 ┆ i64 │ ╞══════╪════════════╪═════════╪═════════╡ │ 13 ┆ 東京 ┆ 6775557 ┆ 7019376 │ └──────┴────────────┴─────────┴─────────┘
元の df
は変わりません。変えたいなら df = df.filter(pl.col("都道府県") == "東京")
のように代入し直します(ここでは行いません)。
男と女の列だけ表示させたいなら、次のようにします(これはpandasと同様です):
df.filter(pl.col("都道府県") == "東京")[["男", "女"]]
shape: (1, 2) ┌─────────┬─────────┐ │ 男 ┆ 女 │ │ --- ┆ --- │ │ i64 ┆ i64 │ ╞═════════╪═════════╡ │ 6775557 ┆ 7019376 │ └─────────┴─────────┘
男女の人口はわかりましたが、合計の人口もほしいですね。「計」という欄を追加しましょう:
df = df.with_columns((pl.col("男") + pl.col("女")).alias("計")) df.head() # 頭の部分だけ見る
shape: (5, 5) ┌──────┬────────────┬─────────┬─────────┬─────────┐ │ 番号 ┆ 都道府県 ┆ 男 ┆ 女 ┆ 計 │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ i64 ┆ i64 ┆ i64 │ ╞══════╪════════════╪═════════╪═════════╪═════════╡ │ 01 ┆ 北海道 ┆ 2450393 ┆ 2733294 ┆ 5183687 │ │ 02 ┆ 青森 ┆ 589143 ┆ 653938 ┆ 1243081 │ │ 03 ┆ 岩手 ┆ 581809 ┆ 624670 ┆ 1206479 │ │ 04 ┆ 宮城 ┆ 1106183 ┆ 1162172 ┆ 2268355 │ │ 05 ┆ 秋田 ┆ 452370 ┆ 504466 ┆ 956836 │ └──────┴────────────┴─────────┴─────────┴─────────┘
df = df.with_columns(pl.Series(name="計", values=df["男"] + df["女"]))
でもよさそうです。
全国の合計も知りたいですね:
df[["男", "女", "計"]].sum()
shape: (1, 3) ┌──────────┬──────────┬───────────┐ │ 男 ┆ 女 ┆ 計 │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 │ ╞══════════╪══════════╪═══════════╡ │ 61420626 ┆ 64507276 ┆ 125927902 │ └──────────┴──────────┴───────────┘
この時点での日本の人口は 125927902 人でした。男より女のほうが多いようですね。
都道府県別に男女比が知りたくなりました。「男女比」という欄を追加しましょう。
df = df.with_columns((pl.col("男") / pl.col("女")).alias("男女比"))
男が少ない都道府県、例えば男女比が 90% 未満のデータを抽出しましょう:
df.filter(pl.col("男女比") < 0.9)
&
(and)や |
(or)で繋げるときは、各不等式を括弧で囲みます:
df.filter((pl.col("男女比") < 0.9) & (pl.col("計") >= 1000000))
抽出されたものは、元の順番(都道府県コード順)に並んでいます。これを男女比の昇順(小さい順)に並べ替えてみます:
df.filter(pl.col("男女比") < 0.9).sort("男女比")
これは次のように分けて書いてもかまいません:
df1 = df.filter(pl.col("男女比") < 0.9) df1.sort("男女比")
降順(大きい順)にするには descending=True
というオプションを付けます:
df.filter(pl.col("男女比") < 0.9).sort("男女比", descending=True)
データ全体を人口の合計でソートし、大きい順に並び替えて、頭の3行を表示しましょう:
df.sort("計", descending=True).head(3)
もう一つデータを読み込んでみます。こちらは各都道府県の面積です:
df2 = pl.read_csv("https://okumuralab.org/~okumura/stat/data/area.csv", dtypes=[pl.Utf8])
df
と df2
を、共通する「番号」「都道府県」で結合(join)し、df
に代入します:
df = df.join(df2, on=["番号", "都道府県"])
人口密度を求めてみましょう:
df = df.with_columns((pl.col("計") / pl.col("面積_km2")).alias("人口密度"))
問題:人口密度の大きい順に並べて、上位10件(.head(10)
)、下位10件(.tail(10)
)を表示してください。