Introduction to Py4Stats

import py4stats as py4st

ここでは Py4Stats の主な機能を紹介します。実装されている関数の一覧は Function reference を参照してください。

py4stats.eda_tools

 探索的データ解析と前処理に関する機能を提供するモジュールです。このモジュールは、複数の DataFrame バックエンドに対して共通の API を提供することを目的として、narwhals ライブラリを用いて実装されています。詳細は Technical Notes: py4stats.eda_tools における narwhals ベースの実装 を参照してください。

 py4stats.diagnose():R言語のdlookr::diagnose()を再現した関数で、データの全般的な状態についての要約を提供します。

import pandas as pd
import numpy as np
from palmerpenguins import load_penguins
penguins = load_penguins() # サンプルデータの読み込み

print(py4st.diagnose(penguins).round(4))
#>                      dtype  missing_count  missing_percent  unique_count  unique_rate
#> species             object              0           0.0000             3       0.8721
#> island              object              0           0.0000             3       0.8721
#> bill_length_mm     float64              2           0.5814           164      47.6744
#> bill_depth_mm      float64              2           0.5814            80      23.2558
#> flipper_length_mm  float64              2           0.5814            55      15.9884
#> body_mass_g        float64              2           0.5814            94      27.3256
#> sex                 object             11           3.1977             2       0.5814
#> year                 int64              0           0.0000             3       0.8721

py4stats.tabyl():R言語の janitor::tabyl()を参考にした、クロス集計表を作成する関数です。

print(py4st.tabyl(penguins, 'island', 'species'))
#> species         Adelie   Chinstrap       Gentoo  All
#> island                                              
#> Biscoe      44 (26.2%)    0 (0.0%)  124 (73.8%)  168
#> Dream       56 (45.2%)  68 (54.8%)     0 (0.0%)  124
#> Torgersen  52 (100.0%)    0 (0.0%)     0 (0.0%)   52
#> All        152 (44.2%)  68 (19.8%)  124 (36.0%)  344

 py4stats.freq_table():R言語のDescTools::Freq()をオマージュした、1変数の度数分布表を計算する関数。度数 freq と相対度数 perc に加えて、それぞれの累積値を計算します。

print(py4st.freq_table(penguins, 'species'))
#>            freq      perc  cumfreq   cumperc
#> species                                     
#> Adelie      152  0.441860      152  0.441860
#> Gentoo      124  0.360465      276  0.802326
#> Chinstrap    68  0.197674      344  1.000000

引数 group を指定すると、グループ別の度数分布表を計算できます。

penguins2 = penguins.assign(bill_length_mm2 = pd.cut(penguins['bill_length_mm'], 6))

print(
    py4st.freq_table(penguins2, ['species', 'bill_length_mm2'], sort = False)
    )
#>                             freq      perc  cumfreq   cumperc
#> species   bill_length_mm2
#> Adelie    (32.072, 38.975]    79  0.523179       79  0.523179
#>           (38.975, 45.85]     71  0.470199      150  0.993377
#>           (45.85, 52.725]      1  0.006623      151  1.000000
#>           (52.725, 59.6]       0  0.000000      151  1.000000
#> Chinstrap (32.072, 38.975]     0  0.000000        0  0.000000
#>           (38.975, 45.85]     13  0.191176       13  0.191176
#>           (45.85, 52.725]     50  0.735294       63  0.926471
#>           (52.725, 59.6]       5  0.073529       68  1.000000
#> Gentoo    (32.072, 38.975]     0  0.000000        0  0.000000
#>           (38.975, 45.85]     40  0.325203       40  0.325203
#>           (45.85, 52.725]     78  0.634146      118  0.959350
#>           (52.725, 59.6]       5  0.040650      123  1.000000

 py4stats.remove_empty():完全に空白な列や行の削除する関数。R言語の janitor::remove_empty() をオマージュした関数で、全ての要素が NaN である列や行をデータフレームから除外します。

penguins2 = penguins.loc[:, ['species', 'body_mass_g']].copy()
penguins2.loc[:, 'empty'] = np.nan
penguins2.loc[344, :] = np.nan

print(penguins2.tail(3))
#>        species  body_mass_g  empty
#> 342  Chinstrap       4100.0    NaN
#> 343  Chinstrap       3775.0    NaN
#> 344        NaN          NaN    NaN

# 完全に空白な行と列を削除。
print(py4st.remove_empty(penguins2, quiet = False).tail(3))
#> Removing 1 empty column(s) out of 3 columns(Removed: empty).
#> Removing 1 empty row(s) out of 345 rows(Removed: 344). 
#>        species  body_mass_g
#> 341  Chinstrap       3775.0
#> 342  Chinstrap       4100.0
#> 343  Chinstrap       3775.0

# 完全に空白な列のみ削除。
print(py4st.remove_empty(penguins2, rows = False, quiet = False).tail(3))
#> Removing 1 empty column(s) out of 3 columns(Removed: empty).
#>        species  body_mass_g
#> 342  Chinstrap       4100.0
#> 343  Chinstrap       3775.0
#> 344        NaN          NaN

# 完全に空白な行のみ削除。
print(py4st.remove_empty(penguins2, cols = False, quiet = False).tail(3))
#> Removing 1 empty row(s) out of 345 rows(Removed: 344). 
#>        species  body_mass_g  empty
#> 341  Chinstrap       3775.0    NaN
#> 342  Chinstrap       4100.0    NaN
#> 343  Chinstrap       3775.0    NaN

 py4stats.remove_constant():定数列の削除。R言語の janitor::remove_constant() をオマージュした関数で、1種類だけの要素からなる列をデータフレームから除外します。

penguins2 = penguins.loc[:, ['species', 'body_mass_g']].copy()
penguins2.loc[:, 'constant'] = 'c'

print(penguins2.head(3))
#>   species  body_mass_g constant
#> 0  Adelie       3750.0        c
#> 1  Adelie       3800.0        c
#> 2  Adelie       3250.0        c

print(py4st.remove_constant(penguins2, quiet = False).head(3))
#> Removing 1 constant column(s) out of 3 column(s)(Removed: constant). 
#>   species  body_mass_g
#> 0  Adelie       3750.0
#> 1  Adelie       3800.0
#> 2  Adelie       3250.0

py4stats.filtering_out():列名に特定の文字列を含む列や、特定の文字列で始まる/終わる列を除外します。実装の一部はR言語の dplyr::select() を参考にしました。

# 列名に 'length' を含む列を除外
print(py4st.filtering_out(penguins, contains = 'length').head(3))
#>   species     island  bill_depth_mm  body_mass_g     sex  year  female
#> 0  Adelie  Torgersen           18.7       3750.0    male  2007       0
#> 1  Adelie  Torgersen           17.4       3800.0  female  2007       1
#> 2  Adelie  Torgersen           18.0       3250.0  female  2007       1

# 列名が 'bill' から始まる列を除外
print(py4st.filtering_out(penguins, starts_with = 'bill').head(3))
#>   species     island  flipper_length_mm  body_mass_g     sex  year  female
#> 0  Adelie  Torgersen              181.0       3750.0    male  2007       0
#> 1  Adelie  Torgersen              186.0       3800.0  female  2007       1
#> 2  Adelie  Torgersen              195.0       3250.0  female  2007       1

# 列名が '_mm' で終わる列を除外
print(py4st.filtering_out(penguins, ends_with = '_mm').head(3))
#>   species     island  body_mass_g     sex  year  female
#> 0  Adelie  Torgersen       3750.0    male  2007       0
#> 1  Adelie  Torgersen       3800.0  female  2007       1
#> 2  Adelie  Torgersen       3250.0  female  2007       1

py4stats.review_wrangling():データ前処理(wrangling)による変更点をレビュー形式で要約して、文字列として出力する関数です。データフレームの形状(行数・列数)の変化に加えて、データ型(dtype)の変化欠測値やカテゴリー変数における水準の増減を報告します。

gentoo = penguins.query("species == 'Gentoo'")
print(py4st.review_wrangling(penguins, gentoo))
#> ========================= Review of wrangling ==========================
#> The shape of DataFrame:
#>    Rows: before 344 -> after 124 (-220)
#>    Cols: before   8 -> after   8 (No change)
#> 
#> No columns were added or removed.
#> 
#> No existing columns had their type changed.
#> 
#> Increase in missing values:
#>   bill_length_mm     before  2 (0.58%) -> after  1 (0.81%)
#>   bill_depth_mm      before  2 (0.58%) -> after  1 (0.81%)
#>   flipper_length_mm  before  2 (0.58%) -> after  1 (0.81%)
#>   body_mass_g        before  2 (0.58%) -> after  1 (0.81%)
#>   sex                before 11 (3.20%) -> after  5 (4.03%)
#> 
#> None of the existing columns decreases in the number of missing values.
#> 
#> The following columns show changes in categories:
#>   species:
#>     addition:  None
#>     removal:  'Adelie' and 'Chinstrap'
#>   island:
#>     addition:  None
#>     removal:  'Torgersen' and 'Dream'
#> ========================================================================

デフォルト設定ではレビュー項目に含まれませんが、items = 'numeric' を指定すると、ASCII 文字列の箱ひげ図を使って数値変数の分布の変化を確認することができます。

print(py4st.review_wrangling(penguins, gentoo, items = 'numeric'))
#> =================== Review of wrangling ====================
#> Boxplot of Numeric values (for reference):
#>   bill_length_mm
#>       before:    32.10|------======:====-----------|   59.60
#>       after:     40.90         |---===:==----------|   59.60
#>   bill_depth_mm
#>       before:    13.10|-------======:=====---------|   21.50
#>       after:     13.10|--===:==-----|                  17.30
#>   flipper_length_mm
#>       before:   172.00|-------====:========--------|  231.00
#>       after:    203.00               |---==:===----|  231.00
#>   body_mass_g
#>       before: 2,700.00|-----====:======------------|6,300.00
#>       after:  3,950.00          |-----==:====------|6,300.00
#>   year
#>       before: 2,007.00|=============:==============|2,009.00
#>       after:  2,007.00|=============:==============|2,009.00
#> ============================================================

py4stats.regression_tools

 py4stats.regression_toolsstatsmodelsライブラリで作成された回帰分析の結果についての表作成と可視化を補助する機能を提供するモジュールです。

 py4stats.compare_ols() :計量経済学の実証論文でよく用いられる、回帰分析の結果を列方向に並べて比較する表を作成します。表のフォーマットについてはR言語のtexreg::screenreg()modelsummary::modelsummary()を参考にしています。同種の機能を提供する Python ライブラリーとしては、R言語の stargazer パッケージをもとにした stargazer ライブラリがあります。

import statsmodels.formula.api as smf

# 回帰分析の実行
fit1 = smf.ols('body_mass_g ~ bill_length_mm + species', data = penguins).fit()
fit2 = smf.ols('body_mass_g ~ bill_length_mm + bill_depth_mm + species', data = penguins).fit()
fit3 = smf.ols('body_mass_g ~ bill_length_mm + bill_depth_mm + species + sex', data = penguins).fit()

compare_tab1 = py4st.compare_ols(list_models = [fit1, fit2, fit3]) # 表の作成
compare_tab1
term model 1 model 2 model 3
Intercept 153.7397 -1,742.7202 *** 843.9812 **
(268.9012) (313.7697) (403.5956)
species[T.Chinstrap] -885.8121 *** -539.6864 *** -245.1516 ***
(88.2502) (86.9425) (84.5952)
species[T.Gentoo] 578.6292 *** 1,492.8283 *** 1,443.3525 ***
(75.3623) (118.4442) (107.7844)
bill_length_mm 91.4358 *** 55.6461 *** 26.5366 ***
(6.8871) (7.2326) (7.2436)
bill_depth_mm 179.0434 *** 87.9328 ***
(19.0997) (20.2192)
sex[T.male] 437.2007 ***
(49.1098)
rsquared_adj 0.7810 0.8258 0.8613
nobs 342 342 333
df 3 4 5

py4stats.compare_ols() の実行結果は PandasDataFrame として出力されるため、.xlsx. ファイルなどに変換することができます。また、用途に応じて表の体裁を調整できるようにしています。詳細については 「回帰分析の比較」 を参照してください。

compare_tab2 = py4st.compare_ols(
    list_models = [fit1, fit2, fit3],
    model_name = ['基本モデル', '嘴の高さ追加', '性別追加'], # モデル名を変更
    stats = 'p_value',        # () 内の値をP-値に変更する
    add_stars = False,        # 有意性のアスタリスクなし
    table_style = 'one_line', # 表スタイルを1行表示に設定 'one' でも可能
    digits = 3                # 小数点以下の桁数を3に設定
    )
compare_tab2
term 基本モデル 嘴の高さ追加 性別追加
Intercept 153.740(0.568) -1,742.720(0.000) 843.981(0.037)
species[T.Chinstrap] -885.812(0.000) -539.686(0.000) -245.152(0.004)
species[T.Gentoo] 578.629(0.000) 1,492.828(0.000) 1,443.353(0.000)
bill_length_mm 91.436(0.000) 55.646(0.000) 26.537(0.000)
bill_depth_mm 179.043(0.000) 87.933(0.000)
sex[T.male] 437.201(0.000)
rsquared_adj 0.781 0.826 0.861
nobs 342 342 333
df 3 4 5

py4stats.coefplot():回帰係数の可視化。R言語の coefplot::coefplot() を参考にしました。

import matplotlib.pyplot as plt
py4st.coefplot(fit3)

coefplot1
plt.rcParams["figure.autolayout"] = True

fig, ax = plt.subplots(1, 2, figsize = (2.2 * 5, 5), dpi = 100)

py4st.coefplot(fit2, ax = ax[0])
ax[0].set_xlim(-900, 1800)

py4st.coefplot(fit3, ax = ax[1], palette = ['#FF6F91', '#F2E5EB'])
ax[1].set_xlim(-900, 1800);

coefplot2

 py4stats.compare_mfx()py4stats.mfxplot() は、それぞれ py4stats.compare_ols()py4stats.coefplot() の一般化線型モデルバージョンです。statsmodels ライブラリの.get_margeff() メソッドから得られた限界効果の推定値を表示します。

penguins['female'] = np.where(penguins['sex'] == 'female', 1, 0)

# ロジスティック回帰の実行
fit_logit1 = smf.logit('female ~ body_mass_g + bill_length_mm + bill_depth_mm', data = penguins).fit()
fit_logit2 = smf.logit('female ~ body_mass_g + bill_length_mm + bill_depth_mm + species', data = penguins).fit()

py4st.compare_mfx([fit_logit1, fit_logit2])
term model 1 model 2
body_mass_g -0.0004 *** -0.0003 ***
(0.0000) (0.0000)
bill_length_mm -0.0053 -0.0357 ***
(0.0036) (0.0070)
bill_depth_mm -0.1490 *** -0.1098 ***
(0.0051) (0.0175)
species[T.Chinstrap] 0.4172 ***
(0.0848)
species[T.Gentoo] 0.3527 ***
(0.1308)
prsquared 0.5647 0.6187
nobs 342 342
df 3 5
plt.rcParams["figure.autolayout"] = True

fig, ax = plt.subplots(1, 2, figsize = (2.2 * 5, 5), dpi = 100)

py4st.mfxplot(fit_logit1, ax = ax[0])
ax[0].set_xlim(-0.2, 0.85)

py4st.mfxplot(fit_logit2, ax = ax[1], palette = ['#FF6F91', '#F2E5EB'])
ax[1].set_xlim(-0.2, 0.85);

coefplot3

Jump to Function reference.