🍌

Polarsで「pandasライブラリ活用入門(第2版)」を書いてみました

2023/12/25に公開

はじめに

Pythonにおけるデータ分析ライブラリといえばpandasですが、最近Rust言語で書かれた高速データ分析ライブラリpolarsというライブラリへの注目が集まっています。
今年はQiitaのアドベントカレンダーも作成されているので、多くの方が気になるライブラリだと言えるでしょう。

私も今年の3月にpandasとpolarsのコード比較記事を執筆しましたが、X(旧Twitter)にて動かないコードがあるとのご指摘を頂きました。
どうやらいくつかの破壊的変更があったようです。
参考書籍の第2版が今年の9月に出版されたので、内容を更新したいと思います。

参考

前回記事との違い

  1. 前回は原著のGitHubに沿ってコードを記載しましたが、今回は書籍の内容に沿ってコードを書いていきます。
    書籍を買われた方にとって、より見やすい構成になるかと思います。

  2. すべてのコードブロックにimport pandas/polarsを記入するので、pandasとpolarsの句は分けないことにします。

  3. DataFrameの表示では、printではなくdisplayを使います。

ライブラリ

  • pandas 2.1.4
  • polars 0.19.19
  • numpy 1.26.2
  • seaborn 0.13.0

第1部 基本的な使い方

第1章 DataFrame

1.2 最初のデータセットをロードする

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("DataFrameの型", type(df))
display("DataFrameの(行数, 列数)", df.shape)
display("DataFrameの列名", df.columns)
display("各列のデータ型", df.dtypes)
display("DataFrameの基本情報", df.info())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("DataFrameの型", type(df))
display("DataFrameの(行数, 列数)", df.shape)
display("DataFrameの列名", df.columns)
display("各列のデータ型", df.dtypes)
display("DataFrameの基本情報", df.describe().transpose(include_header = True))

1.3 列、行、セルを見る

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("先頭の5行を表示する", df.head())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("先頭の5行を表示する", df.head())

1.3.1 名前で列を絞り込む

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
country_df = df["country"]
display("最初の5個の値を表示する", country_df.head())
display("最後の5個の値を表示する", country_df.tail())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
country_df = df["country"]
display("最初の5個の値を表示する", country_df.head(n = 5))
display("最後の5個の値を表示する", country_df.tail(n = 5))
import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
subset = df[["country", "continent", "year"]]
display("国と大陸と年を見る", subset)
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
subset = df[["country", "continent", "year"]]
display("国と大陸と年を見る", subset)

1.3.1.1 1個の値で返されるDataFrameやSeries

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
country_df = df["country"]
display("[] で国の列を抽出した場合", country_df.head())
display("データ型", type(country_df))
country_df_list = df[["country"]]
display("[[]] で国の列を抽出した場合", country_df_list.head())
display("データ型", type(country_df_list))
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
country_df = df["country"]
display("[] で国の列を抽出した場合", country_df.head(n = 5))
display("データ型", type(country_df))
country_df_list = df[["country"]]
display("[[]] で国の列を抽出した場合", country_df_list.head())
display("データ型", type(country_df_list))

1.3.2 複数行の抽出

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("最初の行を取る", df.loc[0])
last_row_index = df.shape[0] - 1
display("最後の行を取る", df.loc[last_row_index])
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("最初の行を取る", df.slice( offset = 0, length = 1))
last_row_index = df.shape[0] - 1
display("最後の行を取る", df.slice( offset = last_row_index, length = 1))
import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("locで最初の行を取得した場合の型", type(df.loc[0]))
display("head()で最初の行を取得した場合の型", type(df.head(n = 1)))
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("sliceで最初の行を取得した場合の型", type(df.slice( offset = 0, length = 1)))
display("head()で最初の行を取得した場合の型", type(df.head(n = 1)))

1.3.2.2 複数行の抽出

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("複数行を抽出する", df.loc[[0, 99, 999]])
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("複数行を抽出する", df[ [0, 99, 999] ])

1.3.3 行番号による複数行の抽出: iloc[]

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("2番目の行を見る", df.iloc[1])
display("100番目の行を見る", df.iloc[99])
display("-1 を使って最後の行を見る", df.iloc[-1])
display("第1行と第100行と第1000行を選択する", df.iloc[[0, 99, 999]])
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("2番目の行を見る", df[ 1 ])
display("100番目の行を見る", df[ 99 ])
display("-1 を使って最後の行を見る", df[ -1 ])
display("第1行と第100行と第1000行を選択する", df[ [0, 99, 999] ])

1.3.4 組み合わせで絞り込む

1.3.4.1 複数列の抽出

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("locと列名で列を絞り込む", df.loc[:, ["year", "pop"]].head(n = 2))
display("ilocと列番号で列を絞り込む", df.iloc[:, [2, 4, -1]].head(n = 2))
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("[]と列名で列を絞り込む", df[:, ["year", "pop"]].head(n = 2))
display("[]と列番号で列を絞り込む", df[:, [2, 4, -1]].head(n = 2))

1.3.4.2 範囲による複数列の抽出

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("第0列から第4列までを絞り込む", df.iloc[:, list(range(5))].head(n = 2))
display("第3列から第5列までを絞り込む", df.iloc[:, list(range(3, 6))].head(n = 2))
display("第0列から第5列まで1つおきに絞り込む", df.iloc[:, list(range(0, 6, 2))].head(n = 2))
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("第0列から第4列までを絞り込む", df[:, list(range(5))].head(n = 2))
display("第3列から第5列までを絞り込む", df[:, list(range(3, 6))].head(n = 2))
display("第0列から第5列まで1つおきに絞り込む", df[:, list(range(0, 6, 2))].head(n = 2))

1.3.4.3 スライスによる複数列の絞り込み

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("最初の3列を絞り込む", df.iloc[:, list(range(3))].head(n = 2))
display("最初の3列をスライス", df.iloc[:, :3].head(n = 2))
display("第3列から第5列までを絞り込む", df.iloc[:, list(range(3, 6))].head(n = 2))
display("第3列から第5列までをスライス", df.iloc[:, 3:6].head(n = 2))
display("第0列から第5列まで1つおきに絞り込む", df.iloc[:, list(range(0, 6, 2))].head(n = 2))
display("第0列から第5列まで1つおきにスライス", df.iloc[:, 0:6:2].head(n = 2))
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("最初の3列を絞り込む", df[:, list(range(5))].head(n = 2))
display("最初の3列をスライス", df[:, :3].head(n = 2))
display("第3列から第5列までを絞り込む", df[:, list(range(3, 6))].head(n = 2))
display("第3列から第5列までをスライス", df[:, 3:6].head(n = 2))
display("第0列から第5列まで1つおきに絞り込む", df[:, list(range(0, 6, 2))].head(n = 2))
display("第0列から第5列まで1つおきにスライス", df[:, 0:6:2].head(n = 2))

1.3.5 列と行の抽出

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("locを用いて、第43行のcountry列の要素を抽出する", df.loc[42, "country"])
display("ilocを用いて、第43行の第0列の要素を抽出する", df.iloc[42, 0])
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("第43行のcountry列の要素を抽出する", df[42, "country"])
display("第43行の第0列の要素を抽出する", df[42, 0])

1.3.5.1 複数行、複数列の抽出

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("第1行、第100行、第1000行を、第1列、第4列、第6列から切り出す", df.iloc[[0, 99, 999], [0, 3, 5]])
display("先の抽出を、列名で実行する場合", df.loc[[0, 99, 999], ["country", "lifeExp", "gdpPercap"]])
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("第1行、第100行、第1000行を、第1列、第4列、第6列から切り出す", df[[0, 99, 999], [0, 3, 5]])
display("先の抽出を、列名で実行する場合", df[[0, 99, 999], ["country", "lifeExp", "gdpPercap"]])

1.4 グループ分けと集約の計算

疑問

  1. 各年度で予期された余命(lifeExp)の平均値は、いくつなのだろうか。人口(pop)やGDP(gdpPercap)の平均値も知りたい
  2. データを大陸(continent)ごとに分け、それぞれに同じ計算を実行したら、どういう結果になるか。
  3. それぞれの大陸のリストに、どれだけの国(country)が入っているか。
import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("グループ分けの前にデータを眺めてみる", df)
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("グループ分けの前にデータを眺めてみる", df)

1.4.1 グループごとの平均値

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
# 各年度で予想された余命の平均値は?
# この疑問に答えるには、
# 1. まずデータを年度によって分割し、
# 2. 毎年の"lifeExp"列を取り出し、
# 3. その平均値を計算する
display("各年度で予想された余命の平均値", df.groupby("year")["lifeExp"].mean())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
# 各年度で予想された余命の平均値は?
# この疑問に答えるには、
# 1. まずデータを年度によって分割し、
# 2. 毎年の"lifeExp"列を取り出し、
# 3. その平均値を計算する
display("各年度で予想された余命の平均値", df.group_by("year").agg( pl.col("lifeExp").mean() ).sort(by = "year"))
import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
# 各年度、大陸別の予想された余命の平均値は?
display("各年度、大陸別で予想された余命の平均値",
        df\
                .groupby(["year", "continent"])[["lifeExp", "gdpPercap"]]\
                        .mean())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
# 各年度、大陸別の予想された余命の平均値は?
display("各年度、大陸別で予想された余命の平均値",
        df\
    .group_by(["year", "continent"])\
    .agg([ pl.col("lifeExp").mean(), pl.mean("gdpPercap")])\
    .sort(by = "year"))

1.4.2 グループごとの度数/頻度

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("各大陸に入っている国の数", df.groupby("continent")["country"].nunique())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("各大陸に入っている国の数",
        df\
            .group_by(by = "continent")\
                .agg( pl.col("country").n_unique() )\
                    .sort(by = "continent"))

第2章 pandasのデータ構造

2.1 データを自作する

2.1.1 Seriesを作る

import pandas as pd
s = pd.Series(["banana", 42])
display("Seriesを作る", s)
import polars as pl
s = pl.Series(name = None, values = ["banana", 42])
display("Seriesを作る", s)

2.1.2 DataFrameを作る

import pandas as pd
scientists = pd.DataFrame({
    "Name": ["Rosaline Franklin", "William Gosset"],
    "Occupation": ["Chemist", "Statistician"],
    "Born": ["1920-07-25", "1876-06-13"],
    "Died": ["1958-04-16", "1937-10-16"]
    })
display("DataFrameは辞書型として作るのが最も一般的である", scientists)
import polars as pl
scientists = pl.DataFrame({
    "Name": ["Rosaline Franklin", "William Gosset"],
    "Occupation": ["Chemist", "Statistician"],
    "Born": ["1920-07-25", "1876-06-13"],
    "Died": ["1958-04-16", "1937-10-16"]
    })
display("DataFrameは辞書型として作るのが最も一般的である", scientists)
# indexはpandasのみ
import pandas as pd
scientists = pd.DataFrame({
    "Occupation": ["Chemist", "Statistician"],
    "Born": ["1920-07-25", "1876-06-13"],
    "Died": ["1958-04-16", "1937-10-16"],
    "Age": [37, 61]
    },
    index = ["Rosaline Franklin", "William Gosset"],
    columns = ["Occupation", "Born", "Died", "Age"])
display("Index付きDataFrameはpandasのみ", scientists)
# polarsのDataFrame作成に関するtips
import polars as pl
data = {
    "Name": ["Rosaline Franklin", "William Gosset"],
    "Occupation": ["Chemist", "Statistician"],
    "Born": ["1920-07-25", "1876-06-13"],
    "Died": ["1958-04-16", "1937-10-16"],
    "Age": [37, 61]
    }
scientists = pl.DataFrame(data = data, schema = {
    "Name": pl.Utf8,
    "Occupation": pl.Utf8,
    "Born": pl.Date,
    "Died": pl.Date,
    "Age": pl.Int64
    })
scientists
display("polarsでDataFrame作成時に各列のデータ型を指定したい場合、辞書型を使用する(その1)", scientists)

scientists = pl.DataFrame(data = data, schema = [
    ("Name", pl.Utf8),
    ("Occupation", pl.Utf8),
    ("Born", pl.Date),
    ("Died", pl.Date),
    ("Age", pl.Int64)
    ])
display("tupleのlistでも各列のデータ型を指定できる(その2)", scientists)

data = [
    pl.Series("Name", ["Rosaline Franklin", "William Gosset"], dtype = pl.Utf8),
    pl.Series("Occupation", ["Chemist", "Statistician"], dtype = pl.Utf8),
    pl.Series("Born", ["1920-07-25", "1876-06-13"], dtype = pl.Date),
    pl.Series("Died", ["1958-04-16", "1937-10-16"], dtype = pl.Date),
    pl.Series("Age", [37, 61], dtype = pl.Int64)
]
scientists = pl.DataFrame(data = data)
display("SeriesのリストとしてDataFrameを定義する", scientists)

2.2 Seriesについて

import pandas as pd
scientists = pd.DataFrame({
    "Occupation": ["Chemist", "Statistician"],
    "Born": ["1920-07-25", "1876-06-13"],
    "Died": ["1958-04-16", "1937-10-16"],
    "Age": [37, 61]
    },
    index = ["Rosaline Franklin", "William Gosset"],
    columns = ["Occupation", "Born", "Died", "Age"])
display("DataFrameの再表示", scientists)

first_row = scientists.loc["William Gosset"]
display("indexがWilliam Gossetの行(第2行)のデータ型", type(first_row))
display("William Gosset行のindex", first_row.index)
display("William Gosset行の各要素", first_row.values)
display("William Gosset行の各キー", first_row.keys())
display("William Gosset行のindexの最初の要素", first_row.index[0])
display("William Gosset行の各キーの最初の要素", first_row.keys()[0])
import polars as pl
data = [
    pl.Series("Name", ["Rosaline Franklin", "William Gosset"], dtype = pl.Utf8),
    pl.Series("Occupation", ["Chemist", "Statistician"], dtype = pl.Utf8),
    pl.Series("Born", ["1920-07-25", "1876-06-13"], dtype = pl.Date),
    pl.Series("Died", ["1958-04-16", "1937-10-16"], dtype = pl.Date),
    pl.Series("Age", [37, 61], dtype = pl.Int64)
]
scientists = pl.DataFrame(data = data)
display("DataFrameの再表示", scientists)

# polarsにはindexがないので、filterで取得する
first_row = scientists.filter( pl.col("Name") == "William Gosset" )
display("indexがWilliam Gossetの行(第2行)のデータ型", type(first_row))
# polarsのkey関係はよくわからなかった
#display("William Gosset行のindex", first_row.index)
#display("William Gosset行の各要素", first_row.values)
#display("William Gosset行の各キー", first_row.keys())
#display("William Gosset行のindexの最初の要素", first_row.index[0])
#display("William Gosset行の各キーの最初の要素", first_row.keys()[0])

2.2.1 Seriesは、ndarrayに似たもの

2.2.1.1 Seriesのメソッド

import pandas as pd
scientists = pd.DataFrame({
    "Occupation": ["Chemist", "Statistician"],
    "Born": ["1920-07-25", "1876-06-13"],
    "Died": ["1958-04-16", "1937-10-16"],
    "Age": [37, 61]
    },
    index = ["Rosaline Franklin", "William Gosset"],
    columns = ["Occupation", "Born", "Died", "Age"])
ages = scientists["Age"]
display("Age列", ages)
print("Age列の平均値\n", ages.mean(), "\n")
print("最小値\n", ages.min(), "\n")
print("最大値\n", ages.max(), "\n")
print("標準偏差\n", ages.std(), "\n")
import polars as pl
data = [
    pl.Series("Name", ["Rosaline Franklin", "William Gosset"], dtype = pl.Utf8),
    pl.Series("Occupation", ["Chemist", "Statistician"], dtype = pl.Utf8),
    pl.Series("Born", ["1920-07-25", "1876-06-13"], dtype = pl.Date),
    pl.Series("Died", ["1958-04-16", "1937-10-16"], dtype = pl.Date),
    pl.Series("Age", [37, 61], dtype = pl.Int64)
]
ages = scientists["Age"]
display("Age列", ages)
print("Age列の平均値\n", ages.mean(), "\n")
print("最小値\n", ages.min(), "\n")
print("最大値\n", ages.max(), "\n")
print("標準偏差\n", ages.std(), "\n")
import pandas as pd
# 初心者でもわかる!やさしい統計用語①「相関関係」 | データで越境者に寄り添うメディア データのじかん
# https://data.wingarc.com/statistics-for-beginners-01-46546
s1 = pd.Series([20, 21, 30, 25, 27, 33, 25, 26, 27, 28])
s2 = pd.Series([20, 32, 50, 30, 56, 62, 50, 47, 25, 36])
display("気温", s1)
display("アイスの販売数量", s2)
print("気温とアイスの販売数量の相関係数\n", s1.corr(s2), "\n")
print("気温とアイスの販売数量の共分散\n", s1.cov(s2), "\n")
display("気温の要約統計量", s1.describe())
display("気温列から重複のない要素を返す", s1.drop_duplicates())
print("気温と販売数量で同じ要素を含んでいるかを確認する\n", s1.equals(s2), "\n")
print("気温の最小値\n", s1.min(), "\n")
print("気温の最大値\n", s1.max(), "\n")
print("気温の算術平均\n", s1.mean(), "\n")
print("気温の中央値\n", s1.median(), "\n")
print("気温の最頻値\n", s1.mode(), "\n")
print("気温の第1四分位数\n", s1.quantile(q = 0.25), "\n")
display("気温列の値を1つ指定して、もう一つの指定値で上書きする", s1.replace(to_replace = {33: 9000}))
display("いくつかの要素を抽出する", s1.sample(n = 3, random_state = 42))
display("ソートする", s1.sort_values())
display("DataFrameに変換する", s1.to_frame())
display("転置する", s1.transpose())
display("ユニーク値を返す", s1.unique())
import polars as pl
# 初心者でもわかる!やさしい統計用語①「相関関係」 | データで越境者に寄り添うメディア データのじかん
# https://data.wingarc.com/statistics-for-beginners-01-46546
s1 = pl.Series("Temperature", [20, 21, 30, 25, 27, 33, 25, 26, 27, 28])
s2 = pl.Series("Number of units sold", [20, 32, 50, 30, 56, 62, 50, 47, 25, 36])
df = pl.DataFrame([
    s1,
    s2
])
display("気温", s1)
display("アイスの販売数量", s2)
display("気温とアイスの販売数量の相関係数", df.select(pl.corr("Temperature", "Number of units sold")))
display("気温とアイスの販売数量の共分散", df.select(pl.cov("Temperature", "Number of units sold")))
display("気温の要約統計量", s1.describe())
#display("気温列から重複のない要素を返す", s1.drop_duplicates())
print("気温と販売数量で同じ要素を含んでいるかを確認する\n", s1.equals(s2), "\n")
print("気温の最小値\n", s1.min(), "\n")
print("気温の最大値\n", s1.max(), "\n")
print("気温の算術平均\n", s1.mean(), "\n")
print("気温の中央値\n", s1.median(), "\n")
print("気温の最頻値\n", s1.mode(), "\n")
print("気温の第1四分位数\n", s1.quantile(quantile = 0.25), "\n")
display("気温列の値を1つ指定して、もう一つの指定値で上書きする", s1.replace(mapping = {33: 9000}))
display("いくつかの要素を抽出する", s1.sample(n = 3, seed = 42))
display("ソートする", s1.sort())
display("DataFrameに変換する", s1.to_frame())
#display("転置する", s1.transpose())
display("ユニーク値を返す", s1.unique())

2.2.2 Seriesを真偽値で絞り込む

ローカルフォルダに保存したデータを読み込むときは、下図のようなフォルダ構造を想定しています。

import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")
display("読み込んだデータを確認する", scientists)
ages = scientists["Age"]
display("年齢列", ages)
display("要約統計量", ages.describe())
print("要約統計量\n", ages.mean(), "\n")
display("平均より高い値だけ絞り込む", ages[ages > ages.mean()])
print("絞り込みのデータ型\n", type(ages > ages.mean()), "\n")
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")
display("読み込んだデータを確認する", scientists)
ages = scientists["Age"]
display("年齢列", ages)
display("要約統計量", ages.describe())
print("要約統計量\n", ages.mean(), "\n")
display("平均より高い値だけ絞り込む", ages.filter( ages > ages.mean() ))
print("絞り込みのデータ型\n", type(ages > ages.mean()), "\n")

2.2.3 演算の自動的な整列とベクトル化(ブロードキャスティング)

2.2.3.1 同じ長さのベクトル
import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")
ages = scientists["Age"]
display("ベクトルの和", ages + ages)
display("アダマール積", ages + ages)
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")
ages = scientists["Age"]
display("ベクトルの和", ages + ages)
display("アダマール積", ages + ages)

2.2.3.2 ベクトルと整数値(スカラー)

import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")
ages = scientists["Age"]
display("ベクトルの全ての要素にスカラーを加える", ages + 100)
display("ベクトルとスカラーの積", ages * 2)
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")
ages = scientists["Age"]
display("ベクトルの全ての要素にスカラーを加える", ages + 100)
display("ベクトルとスカラーの積", ages * 2)

2.3 DataFrameについて

2.3.1 DataFrameの構成要素

import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")
display("index列を取り出す", scientists.index)
display("列名", scientists.columns)
display("要素", scientists.values)
#display("arrow形式", scientists.to_arrow())
#display("辞書型", scientists.to_dicts())
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")
#display("index列を取り出す", scientists.index)
display("列名", scientists.columns)
display("要素", scientists.to_numpy())
display("arrow形式", scientists.to_arrow())
display("辞書型", scientists.to_dicts())

2.3.2 DataFrameを真偽値で絞り込む

import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")
display("真偽値で絞り込む", scientists.loc[ scientists["Age"] > scientists["Age"].mean() ])
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")
display("真偽値で絞り込む", scientists.filter( scientists["Age"] > scientists["Age"].mean() ).sort("Name"))
display("マイナーチェンジ 1", scientists.filter( pl.col("Age") > scientists["Age"].mean() ).sort("Name"))
display("マイナーチェンジ 2", scientists.filter( scientists["Age"] > pl.col("Age").mean() ).sort("Name"))
display("マイナーチェンジ 3", scientists.filter( pl.col("Age") > pl.col("Age").mean() ).sort("Name"))
display("マイナーチェンジ 4", scientists.filter( pl.col("Age") > pl.mean("Age") ).sort("Name"))

2.4 SeriesとDataFrameの書き換え

import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")
display("各列のデータ型", scientists.dtypes)

born_datetime = pd.to_datetime(scientists["Born"], format = "%Y-%m-%d")
display("生年月日のDateTime型", born_datetime)

died_datetime = pd.to_datetime(scientists["Died"], format = "%Y-%m-%d")
display("死亡日のDateTime型", died_datetime)

scientists["born_dt"], scientists["died_dt"] = (
    born_datetime,
    died_datetime
)
display("列追加後のDataFrame", scientists)
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")
display("各列のデータ型", scientists.dtypes)

born_datetime = scientists["Born"].str.to_date(format = "%Y-%m-%d")
display("生年月日のDateTime型", born_datetime)

died_datetime = scientists["Died"].str.to_date(format = "%Y-%m-%d")
display("死亡日のDateTime型", died_datetime)

scientists = scientists.with_columns([
    born_datetime.alias("born_dt"),
    died_datetime.alias("died_dt")
])
display("列追加後のDataFrame", scientists)

2.4.2 列を直接変更する

import pandas as pd
import numpy as np
scientists = pd.read_csv("../data/scientists.csv")

scientists["born_dt"], scientists["died_dt"] = (
    pd.to_datetime(scientists["Born"], format = "%Y-%m-%d"),
    pd.to_datetime(scientists["Died"], format = "%Y-%m-%d")
)

scientists["age_days"] = (
    scientists["died_dt"] - scientists["born_dt"]
)
scientists["age_years"] = (
    scientists["age_days"] / np.timedelta64(1, "ns") / (365.2425 * 24 * 60 * 60 * 1e9)
)
display("生年月日から死亡日までの年数を追加したDataFrame", scientists)
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")

scientists = scientists.with_columns([
    scientists["Born"].str.to_date(format = "%Y-%m-%d").alias("born_dt"),
    scientists["Died"].str.to_date(format = "%Y-%m-%d").alias("died_dt")
])


scientists = scientists.with_columns([
    (scientists["died_dt"] - scientists["born_dt"]).alias("age_days")
])
scientists = scientists.with_columns([
    (scientists["age_days"].dt.days() / 365.0 ).alias("age_years")
])
display("列追加後のDataFrame", scientists)

2.4.4 値を捨てる

import pandas as pd
scientists = pd.read_csv("../data/scientists.csv")

print("元のデータの列名\n", scientists.columns, "\n")
scientists_dropped = scientists.drop(["Age"], axis = "columns")
print("Age列削除後のデータの列名\n", scientists_dropped.columns, "\n")
import polars as pl
import numpy as np
scientists = pl.read_csv("../data/scientists.csv")
print("元のデータの列名\n", scientists.columns, "\n")
scientists_dropped = scientists.drop(["Age"])
print("Age列削除後のデータの列名\n", scientists_dropped.columns, "\n")

2.5 データのエクスポートとインポート

2.5.1 pickle

polarsにはpickle形式での保存方法は無い

2.5.1.1 Series

2.5.1.2 DataFrame

2.5.1.3 pickleデータを読み込む

2.5.2 CSV

import pandas as pd
import numpy as np
scientists = pd.read_csv("../data/scientists.csv")
scientists["born_dt"], scientists["died_dt"] = (
    pd.to_datetime(scientists["Born"], format = "%Y-%m-%d"),
    pd.to_datetime(scientists["Died"], format = "%Y-%m-%d")
)
scientists["age_days"] = (
    scientists["died_dt"] - scientists["born_dt"]
)
scientists["age_years"] = (
    scientists["age_days"] / np.timedelta64(1, "ns") / (365.2425 * 24 * 60 * 60 * 1e9)
)

scientists.to_csv("../output/scientists_df_no_index.csv", index = False)
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")

scientists = scientists.with_columns([
    scientists["Born"].str.to_date(format = "%Y-%m-%d").alias("born_dt"),
    scientists["Died"].str.to_date(format = "%Y-%m-%d").alias("died_dt")
])
scientists = scientists.with_columns([
    (scientists["died_dt"] - scientists["born_dt"]).alias("age_days")
])
scientists = scientists.with_columns([
    (scientists["age_days"].dt.days() / 365.0 ).alias("age_years")
])

display("age_daysの値が13779dでありCSV保存の時にエラーが出る", scientists["age_days"].head(2))
scientists = scientists.replace(
    "age_days", scientists["age_days"].dt.days()
)
display("age_daysの値を整数型に変換する", scientists["age_days"].head(2))

scientists.write_csv("../output/scientists_df.csv")

2.5.3 Excel

import pandas as pd
import numpy as np
scientists = pd.read_csv("../data/scientists.csv")
scientists["born_dt"], scientists["died_dt"] = (
    pd.to_datetime(scientists["Born"], format = "%Y-%m-%d"),
    pd.to_datetime(scientists["Died"], format = "%Y-%m-%d")
)
scientists["age_days"] = (
    scientists["died_dt"] - scientists["born_dt"]
)
scientists["age_years"] = (
    scientists["age_days"] / np.timedelta64(1, "ns") / (365.2425 * 24 * 60 * 60 * 1e9)
)

scientists.to_excel("../output/scientists_df_no_index.xlsx", index = False)
import polars as pl
scientists = pl.read_csv("../data/scientists.csv")

scientists = scientists.with_columns([
    scientists["Born"].str.to_date(format = "%Y-%m-%d").alias("born_dt"),
    scientists["Died"].str.to_date(format = "%Y-%m-%d").alias("died_dt")
])
scientists = scientists.with_columns([
    (scientists["died_dt"] - scientists["born_dt"]).alias("age_days")
])
scientists = scientists.with_columns([
    (scientists["age_days"].dt.days() / 365.0 ).alias("age_years")
])

'''
display("age_daysの値が13779dでありCSV保存の時にエラーが出る", scientists["age_days"].head(2))
scientists = scientists.replace(
    "age_days", scientists["age_days"].dt.days()
)
display("age_daysの値を整数型に変換する", scientists["age_days"].head(2))
'''
scientists.write_excel("../output/scientists_df.xlsx")

第4章 「整然データ」を作る

4.1 列に(変数ではなく)値が入っているとき

4.1.1 1列に集める

import pandas as pd
pew = pd.read_csv("../data/pew.csv")
display("最初の5列を表示する", pew.iloc[:, 0:5])
pew_long = pew.melt(id_vars = "religion", var_name = "income", value_name = "count")
display("縦持ちデータを表示する", pew_long)
import polars as pl
pew = pl.read_csv("../data/pew.csv")
display("最初の5列を表示する", pew[:, 0:5])
pew_long = pew.melt(id_vars = "religion", variable_name = "income", value_name = "count")
display("縦持ちデータを表示する", pew_long)

4.1.2 複数の列を残す

import pandas as pd
billboard = pd.read_csv("../data/billboard.csv")
display("列および行の先頭部分を表示する", billboard.iloc[0:5, 0:16])
billboard_long = billboard\
    .melt(id_vars = ["year", "artist", "track", "time", "date.entered"],
           var_name = "week",
             value_name = "rating")
display("縦持ちデータを表示する", billboard_long)
import polars as pl
billboard = pl.read_csv("../data/billboard.csv")
display("列および行の先頭部分を表示する", billboard[0:5, 0:16])
billboard_long = billboard\
    .melt(id_vars = ["year", "artist", "track", "time", "date.entered"],
           variable_name = "week",
             value_name = "rating")
display("縦持ちデータを表示する", billboard_long)

4.2 複数の変数を含む列があるとき

import pandas as pd
ebola = pd.read_csv("../data/country_timeseries.csv")
display("先頭のデータを表示する", ebola.head(n = 3))
ebola_long = ebola.melt(id_vars = ["Date", "Day"])
display("縦持ちデータを表示する", ebola_long.head(n = 3))
import polars as pl
ebola = pl.read_csv("../data/country_timeseries.csv")
display("先頭のデータを表示する", ebola.head(n = 3))
ebola_long = ebola.melt(id_vars = ["Date", "Day"])
display("縦持ちデータを表示する", ebola_long.head(n = 3))

4.2.1 列を分解して個別に追加する

import pandas as pd
ebola = pd.read_csv("../data/country_timeseries.csv")
ebola_long = ebola.melt(id_vars = ["Date", "Day"])

variable_split = ebola_long["variable"].str.split("_")
display("発症_国名のデータを分割する", variable_split.head(n = 3))
print("分割した列のデータ型", type(variable_split), "\n")
print("分割した列の最初の要素のデータ型", type(variable_split[0]), "\n")

status_values = variable_split.str.get(0)
country_values = variable_split.str.get(1)
ebola_long["status"] = status_values
ebola_long["country"] = country_values
display("分割した列を縦持ちデータに追加する", ebola_long)
import polars as pl
ebola = pl.read_csv("../data/country_timeseries.csv")
ebola_long = ebola.melt(id_vars = ["Date", "Day"])

variable_split = ebola_long["variable"].str.split("_")
display("発症_国名のデータを分割する", variable_split.head(n = 3))
print("分割した列のデータ型", type(variable_split), "\n")
print("分割した列の最初の要素のデータ型", type(variable_split[0]), "\n")

df_variable_split = ebola_long["variable"]\
        .str.split_exact("_", 1)\
                .struct.rename_fields(["status", "country"])\
                            .alias("fields")\
                                .to_frame().unnest("fields")

status_values = df_variable_split["status"]
country_values = df_variable_split["country"]
ebola_long = ebola_long.with_columns([
    status_values,
    country_values
])
display("分割した列を縦持ちデータに追加する", ebola_long)

4.2.2 分割と結合を一度に行う

import pandas as pd
ebola = pd.read_csv("../data/country_timeseries.csv")

ebola_long = ebola.melt(id_vars = ["Date", "Day"])

variable_split = ebola_long.variable.str.split("_", expand = True)
ebola_long[ ["status", "country"] ] = variable_split
display("分割した列を縦持ちデータに追加する", ebola_long.head(n = 3))
import polars as pl
ebola = pl.read_csv("../data/country_timeseries.csv")

#polarsで同様のことを行う場合 その1
#Series表記の場合、to_fram()とunnest()メソッドが必要
ebola_long = ebola.melt(id_vars = ["Date", "Day"])

ebola_long = ebola_long.with_columns(
    ebola_long["variable"]\
        .str.split_exact("_", 1)\
                .struct.rename_fields(["status", "country"])\
                            .alias("fields")\
                                .to_frame()\
                                        .unnest("fields")
)

display("分割した列を縦持ちデータに追加する", ebola_long.head(n = 3))
import polars as pl
ebola = pl.read_csv("../data/country_timeseries.csv")

#polarsで同様のことを行う場合 その2
#pl.cpl()を用いる場合、unnestは最後につける
ebola_long = ebola.melt(id_vars = ["Date", "Day"])

ebola_long = ebola_long.with_columns(
    pl.col("variable")\
        .str.split_exact("_", 1)\
                .struct.rename_fields(["status", "country"])\
                            .alias("fields")
).unnest("fields")

display("分割した列を縦持ちデータに追加する", ebola_long.head(n = 3))

4.3 行と列の両方に変数があるとき

import pandas as pd
weather = pd.read_csv("../data/weather.csv")
display("元のデータの表示", weather.head(n = 3))

weather_melt = weather.melt(
    id_vars = ["id", "year", "month", "element"],
    var_name = "day",
    value_name = "temp"
)
display("縦持ちデータの表示", weather_melt.head(n = 3))

weather_tidy = weather_melt.pivot(
    index = ["id", "year", "month", "day"],
    columns = "element",
    values = "temp"
)
display("気温の最大値・最小値をピボット展開したデータの表示", weather_tidy.head(n = 3))

#pandasで層別indexを分解したい場合
#weather_tidy_flat = weather_tidy.reset_index()
import polars as pl
weather = pl.read_csv("../data/weather.csv")
display("元のデータの表示", weather.head(n = 3))

weather_melt = weather.melt(
    id_vars = ["id", "year", "month", "element"],
    variable_name = "day",
    value_name = "temp"
)
display("縦持ちデータの表示", weather_melt.head(n = 3))

weather_tidy = weather_melt.pivot(
    index = ["id", "year", "month", "day"],
    columns = "element",
    values = "temp"
)
display("気温の最大値・最小値をピボット展開したデータの表示", weather_tidy.head(n = 3))
import pandas as pd
weather = pd.read_csv("../data/weather.csv")
weather_melt = weather.melt(
    id_vars = ["id", "year", "month", "element"],
    var_name = "day",
    value_name = "temp"
)

# 先ほどのmelt, pivotをメソッドチェーンで記述する
# データ処理の方向性が決まったら、処理の一連をチェーンする形で書き直すのが良いだろう
weather_tidy = (
    weather_melt\
        .pivot(index = ["id", "year", "month", "day"],
                     columns = "element",
                     values = "temp")\
                        .reset_index()
)
display("気温の最大値・最小値をピボット展開したデータの表示", weather_tidy.head(n = 3))
import polars as pl
weather = pl.read_csv("../data/weather.csv")
weather_melt = weather.melt(
    id_vars = ["id", "year", "month", "element"],
    variable_name = "day",
    value_name = "temp"
)

# 先ほどのmelt, pivotをメソッドチェーンで記述する
# データ処理の方向性が決まったら、処理の一連をチェーンする形で書き直すのが良いだろう
weather_tidy = weather.melt(
    id_vars = ["id", "year", "month", "element"],
    variable_name = "day",
    value_name = "temp"
    ).pivot(
        index = ["id", "year", "month", "day"],
        columns = "element",
        values = "temp"
        )
display("気温の最大値・最小値をピボット展開したデータの表示", weather_tidy.head(n = 3))

第5章 関数を適用(apply)する

5.1 関数の初歩

# pandas, polarsは使用しないブロック
def my_function():
    pass

def my_sq(x):
    return x ** 2

def avg_2(x, y):
    return (x + y)/ 2

my_calc_1 = my_sq(4)
print(my_calc_1)

my_calc_2 = avg_2(10,20)
print(my_calc_2)

5.2 applyの基本

import pandas as pd
df = pd.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })
display("元データ", df["a"])
display("各要素を2乗する", df["a"] ** 2)
import polars as pl
df = pl.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })
display("元データ", df["a"])
display("各要素を2乗する", df["a"] ** 2)

5.2.1 関数をSeriesに適用する

import pandas as pd
df = pd.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })

print("抽出した列aのデータ型", type(df["a"]))
print("抽出した1行目のデータ型", type(df.iloc[0, :]))

def my_sq(x):
    return x ** 2
sq = df["a"].apply(my_sq)
display("Seriesの各要素を2乗したSeries", sq)

def my_exp(x, e):
    return x ** e
ex = df["a"].apply(my_exp, e = 3)
display("Seriesの各要素を3乗したSeries", ex)
import polars as pl
df = pl.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })

print("抽出した列aのデータ型", type(df["a"]))
print("抽出した1行目のデータ型", type(df[0, :]))

def my_sq(x):
    return x ** 2
# polarsのapplyは将来的に削除される
#sq = df["a"].apply(my_sq)
sq = df["a"].map_elements(lambda x: my_sq(x))
display("Seriesの各要素を2乗したSeries", sq)

def my_exp(x, e):
    return x ** e
ex = df["a"].map_elements(lambda elemtn: my_exp(x = elemtn, e = 3))
display("Seriesの各要素を3乗したSeries", ex)

5.2.2 関数をDataFrameに適用する

5.2.2.1 列ごとの演算

polarsのapplyやmap_rowsには列ごとの処理が無いようなので、この節は省略

5.2.2.2 行ごとの演算

import pandas as pd
df = pd.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })

def avg_2_apply(row):
    x = row[0]
    y = row[1]
    return (x + y)/2
display("行ごとに関数を適用する", df.apply(avg_2_apply, axis = 1))
import polars as pl
df = pl.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })

def avg_2_apply(row):
    x = row[0]
    y = row[1]
    return (x + y)/2
#display("行ごとに関数を適用する", df.apply(lambda row: avg_2_apply(row)))
display("行ごとに関数を適用する", df.map_rows(lambda row: avg_2_apply(row)))

5.3 関数のベクトル化

import pandas as pd
df = pd.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })

def avg_2(x, y):
    return (x + y)/2
display("2つの列をベクトルのように扱う", avg_2(df["a"], df["b"]))
import polars as pl
df = pl.DataFrame({
    "a": [10, 20, 30],
    "b": [20, 30, 40]
    })

def avg_2(x, y):
    return (x + y)/2
display("2つの列をベクトルのように扱う", avg_2(df["a"], df["b"]))
#display("別の書き方 その1", df.apply(lambda row: avg_2(row[0], row[1])))
display("別の書き方 その2", df.map_rows(lambda row: avg_2(row[0], row[1])))

第2部 データ操作

第6章 データの組立

6.1 データセットを組み合わせる

6.2 連結

import pandas as pd
df1 = pd.read_csv("../data/concat_1.csv")
df2 = pd.read_csv("../data/concat_2.csv")
df3 = pd.read_csv("../data/concat_3.csv")

display("元データ その1", df1)
display("元データ その2", df2)
display("元データ その3", df3)
import polars as pl
df1 = pl.read_csv("../data/concat_1.csv")
df2 = pl.read_csv("../data/concat_2.csv")
df3 = pl.read_csv("../data/concat_3.csv")

display("元データ その1", df1)
display("元データ その2", df2)
display("元データ その3", df3)

6.2.1 DataFrameの構成要素(復習)

index, columns, valuesなので省略

6.2.2 行の追加

import pandas as pd
df1 = pd.read_csv("../data/concat_1.csv")
df2 = pd.read_csv("../data/concat_2.csv")
df3 = pd.read_csv("../data/concat_3.csv")

row_concat = pd.concat([df1, df2, df3])
display("行方向に結合したデータ(縦結合)", row_concat)

new_row_df = pd.DataFrame({
    "A": ["n1"],
    "B": ["n2"],
    "C": ["n3"],
    "D": ["n4"]
})
display("行方向に結合したデータ(縦結合)", pd.concat([df1, new_row_df]))
import polars as pl
df1 = pl.read_csv("../data/concat_1.csv")
df2 = pl.read_csv("../data/concat_2.csv")
df3 = pl.read_csv("../data/concat_3.csv")

row_concat = pl.concat(items = [df1, df2, df3], how = 'vertical')
display("行方向に結合したデータ(縦結合)", row_concat)

new_row_df = pl.DataFrame({
    "A": ["n1"],
    "B": ["n2"],
    "C": ["n3"],
    "D": ["n4"]
})
display("行方向に結合したデータ(縦結合)", pl.concat(items = [df1, new_row_df], how = 'vertical'))

6.2.2.1 インデックスの再設定

polarsにはindexが無いので省略

6.2.3 列の追加

import pandas as pd
df1 = pd.read_csv("../data/concat_1.csv")
df2 = pd.read_csv("../data/concat_2.csv")
df3 = pd.read_csv("../data/concat_3.csv")

#polarsでは同じ列名が許されないので、pandasと同様の横結合はできない
col_concat = pd.concat([df1, df2, df3], axis = "columns")
display("列方向に結合したデータ(横結合)", col_concat)
import polars as pl
df1 = pl.read_csv("../data/concat_1.csv")
df2 = pl.read_csv("../data/concat_2.csv")
df3 = pl.read_csv("../data/concat_3.csv")

# polarsで同名の列を横結合する場合、renameする必要がある
col_concat = pl.concat(items = [df1,
                                 df2.rename(mapping = {"A": "A2", "B": "B2", "C": "C2", "D": "D2"}),
                                   df3.rename(mapping = {"A": "A3", "B": "B3", "C": "C3", "D": "D3"})],
                        how = 'horizontal')
display("列方向に結合したデータ(横結合)", col_concat)

6.2.4 インデックスが異なる連結

6.2.4.1 列に相違のある行を連結する

import pandas as pd
df1 = pd.read_csv("../data/concat_1.csv")
df2 = pd.read_csv("../data/concat_2.csv")
df3 = pd.read_csv("../data/concat_3.csv")

df1.columns = ["A", "B", "C", "D"]
df2.columns = ["E", "F", "G", "H"]
df3.columns = ["A", "C", "F", "H"]

display("元データ その1", df1)
display("列名を変更したデータ その1", df2)
display("列名を変更したデータ その2", df3)

# 列名が異なるDataFrameを盾結合する場合は'diagonal'を指定する
row_concat = pd.concat([df1, df2, df3])
display("行方向に結合したデータ(縦結合)", row_concat)
import polars as pl
df1 = pl.read_csv("../data/concat_1.csv")
df2 = pl.read_csv("../data/concat_2.csv")
df3 = pl.read_csv("../data/concat_3.csv")

df1.columns = ["A", "B", "C", "D"]
df2.columns = ["E", "F", "G", "H"]
df3.columns = ["A", "C", "F", "H"]

display("元データ その1", df1)
display("列名を変更したデータ その1", df2)
display("列名を変更したデータ その2", df3)

# 列名が異なるDataFrameを盾結合する場合は'diagonal'を指定する
row_concat = pl.concat(items = [df1, df2, df3], how = 'diagonal')
display("行方向に結合したデータ(縦結合)", row_concat)

6.2.4.2 行に相違のある列を連結する

polarsにindexは無いので、この節は省略

6.3 データが観測単位ごとの表に分かれている場合

github上に書籍で紹介されているデータがないのでスキップする

第8章 groupby演算による分割-適用-結合

8.1 集約

8.1.1 1この変数で分割する基本的な集約

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("元データ", df)

avg_life_by_year = df.groupby(by = "year")["lifeExp"].mean()
display("年度別に平均余命を集計する", avg_life_by_year)

print("記録がある年度の一覧", df["year"].unique())
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("元データ", df)

avg_life_by_year = df.group_by(by = "year").agg( pl.col("lifeExp").mean() ).sort(by = "year")
display("年度別に平均余命を集計する", avg_life_by_year)

print("記録がある年度の一覧", df["year"].unique().to_list())
import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")

y1952 = df[ df["year"] == 1952 ]
display("1952年のデータ", y1952)

y1952_mean = y1952["lifeExp"].mean()
print("1952年の余命平均", y1952_mean)
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")

y1952 = df.filter( pl.col("year") == 1952 )
display("1952年のデータ", y1952)

y1952_mean = y1952["lifeExp"].mean()
print("1952年の余命平均", y1952_mean)

8.1.2 組込みの集約メソッド

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")
display("年度別の国数", df.groupby(by = "year")["country"].count().head(1) )
display("年度別の人口のカウント", df.groupby(by = "year")["country"].size().head(1) )
display("年度別の人口の平均", df.groupby(by = "year")["pop"].mean().head(1) )
display("年度別の人口の標準偏差", df.groupby(by = "year")["pop"].std().head(1) )
display("年度別の人口の最小値", df.groupby(by = "year")["pop"].min().head(1) )
display("年度別の人口の四分位数", df.groupby(by = "year")["pop"].quantile(q = 0.25).head(1) )
display("年度別の人口中央値", df.groupby(by = "year")["pop"].quantile(q = 0.5).head(1) )
display("年度別の人口第3四分位数", df.groupby(by = "year")["pop"].quantile(q = 0.75).head(1) )
display("年度別の人口最大値", df.groupby(by = "year")["pop"].max().head(1) )
display("年度別の人口の和", df.groupby(by = "year")["pop"].sum().head(1) )
display("年度別の人口の分散", df.groupby(by = "year")["pop"].var().head(1) )
display("年度別の人口の不偏標準偏差", df.groupby(by = "year")["pop"].sem().head(1) )
display("年度別の人口の基本統計量", df.groupby(by = "year")["pop"].describe().head(1) )
display("年度別の人口の最初の要素", df.groupby(by = "year")["pop"].first().head(1) )
display("年度別の人口の最後の要素", df.groupby(by = "year")["pop"].last().head(1) )
display("年度別の第1番の要素", df.groupby(by = "year").nth(1).head(1) )
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")
display("年度別の国数", df.group_by(by = "year").agg( pl.col("country").count() ).head(1) )
#display("年度別の人口のカウント", df.group_by(by = "year").agg( pl.col("country").size() ) )
display("年度別の人口の平均", df.group_by(by = "year").agg( pl.col("pop").mean() ).head(1) )
display("年度別の人口の標準偏差", df.group_by(by = "year").agg( pl.col("pop").std() ).head(1) )
display("年度別の人口の最小値", df.group_by(by = "year").agg( pl.col("pop").min() ).head(1) )
display("年度別の人口の四分位数", df.group_by(by = "year").agg( pl.col("pop").quantile(quantile = 0.25) ).head(1) )
display("年度別の人口中央値", df.group_by(by = "year").agg( pl.col("pop").quantile(quantile = 0.5) ).head(1) )
display("年度別の人口第3四分位数", df.group_by(by = "year").agg( pl.col("pop").quantile(quantile = 0.75) ).head(1) )
display("年度別の人口最大値", df.group_by(by = "year").agg( pl.col("pop").max() ).head(1) )
display("年度別の人口の和", df.group_by(by = "year").agg( pl.col("pop").sum() ).head(1) )
display("年度別の人口の分散", df.group_by(by = "year").agg( pl.col("pop").var() ).head(1) )
#display("年度別の人口の不偏標準偏差", df.group_by(by = "year").agg( pl.col("pop").sem() ).head(1) )
#display("年度別の人口の基本統計量", df.group_by(by = "year").agg( pl.col("pop").describe() ).head(1) )
display("年度別の人口の最初の要素", df.group_by(by = "year").agg( pl.col("pop").first() ).head(1) )
display("年度別の人口の最後の要素", df.group_by(by = "year").agg( pl.col("pop").last() ).head(1) )
#display("年度別の人口のn番目の要素", df.group_by(by = "year").agg( pl.col("pop").nth() ).head(1) )

8.1.3 集約関数

8.1.3.1 他のライブラリの関数

numpyとの連携が良くわからなかったので省略

8.1.3.2 カスタムのユーザー関数

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")

def my_mean(values):
    n = len(values)

    sum = 0
    for value in values:
        sum += value
    
    return sum / n
display("カスタムのユーザー関数", df.groupby("year")["lifeExp"].agg(my_mean) )
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")

def my_mean(values):
    n = len(values)

    sum = 0
    for value in values:
        sum += value
    
    return sum / n
display("カスタムのユーザー関数", df.group_by("year").agg( pl.col("lifeExp").map_elements(my_mean)).sort("year") )

8.1.4 複数の関数を同時に計算する

import pandas as pd
df = pd.read_csv("../data/gapminder.tsv", sep = "\t")

gdf = df\
    .groupby("year")\
        .agg({
            "lifeExp": "mean",
            "pop": "median",
            "gdpPercap": "median"
        })
display("年度別に、余命のデータ数と、人口の平均値とGDPの中央値を計算する", gdf)
import polars as pl
df = pl.read_csv("../data/gapminder.tsv", separator = "\t")

gdf = df\
    .group_by("year")\
        .agg([pl.col("lifeExp").count().alias("mean"),
              pl.col("pop").mean().alias("mean"),
              pl.col("gdpPercap").std().alias("median")])
display("年度別に、余命の平均値と、人口の平均値とGDPの中央値を計算する", gdf)

8.1.5 .agg/aggregateでdictを使う

8.1.5.1 DataFrameに対するdictの指定

省略

8.2 変換(transform)

8.2.1 zスコアの例

polarsにはtransformが無いので省略

8.2.2 欠損値の例

この句はpandasで書いている

8.3 フィルタリング

import pandas as pd
import seaborn as sns

tips = pd.DataFrame( sns.load_dataset("tips") )
print("元データの要素数", tips.shape)
display("size列の集計", tips["size"].value_counts())

tips_filtered = tips.groupby(by = "size").filter(lambda x: x["size"].count() >= 30)
display("要素数が30個以上のデータを抽出した", tips_filtered)
print("要素数", tips_filtered.shape)
import polars as pl
import seaborn as sns

tips = pl.DataFrame( sns.load_dataset("tips") )
print("元データの要素数", tips.shape)
display("size列の集計", tips["size"].value_counts())

tips_filtered = tips.group_by(by = "size").agg( pl.col("size").count().alias("counts") ).filter( pl.col("counts") >= 30 )
display("要素数が30個以上のデータを抽出した", tips_filtered)
print("要素数", tips_filtered.shape)

おわりに

3月の記事と比較すると、私のPython力(ちから)が向上しているのを感じます。
継続は力なりですね。

Discussion