pythonのprastクラスによる集計(1) 度数と割合の表示

2018/09/17

 pythonで定番の単純な集計を行う目的で prast.py を自作しました。

 その prast.py の使い方を随時 記します。

 当Webページで紹介するスクリプトや素材データ一式は、
prast01.zip という圧縮ファイルに同梱しておきます。

(当サイトでは触れませんが、Excelに出力するスクリプトも同梱してあります。)

    


《このページの目次》


    

はじめに

 pythonというプログラミング言語には豊富な統計処理のライブラリがあります。

 ただ、ちょっと物足りなく感じる点があるのも確かです。

 カイ二乗検定で残差関連の情報が取得できないとか、
平均値の差の検定(t検定)で片側検定を指定するオプションがないなど。

 また、ごく簡単な定番の集計を行うのでも、
実際にプログラムを書いてみると結構面倒です。
これは python に限ったことではありませんが……

 ということで、prast.py を自作しました。

 prast.pyには Prastというクラスの定義が書かれています。

 以降でそのクラスの使い方を記します。

    

 動作確認した環境は次のとおり。

目次に戻る


1. 処理するソースデータ(csvファイル)

 圧縮ファイル prast01.zip には data01.csv が含まれています。

 乱数により生成したデータです。

 400人分のデータがcsv形式で書かれていて、列は次の6項目。

 ID, gender(性別), height(身長), weight(体重), area(地域), opinion(意見)

 データ中に全角文字はありません。半角の英数字で書かれています。

 genderなどの文字データの各列の内訳は下のとおり。

gender
    m,男性
    f,女性
area
    C,海岸
    H,丘陵
    L,湖畔
    R,川沿い
    U,都市部
opinion
    y,賛成
    n,反対
    h,保留

 ID, area には欠損値がありませんが、
その他の列には欠損値(空白セル)が若干あります。

目次に戻る


2. データへの別名割り当て

 csvデータの中身は、mが男性、fが女性などのように
英字1文字の省略形で書かれています。

 省略形をそのまま集計表に持ち込んでも訳がわからないので変更します。

 ということで、省略形とちゃんとした単語(別名)の対応表を
csvデータとは別に設けておいて、
それに即して別名を割り当てます。

 圧縮ファイルに入っている data01_i.txt が割り当て用のファイルで、
中身は下のとおり。

 1gender
 2m,男性
 3f,女性
 4_na,記載なし
 5
 6area
 7C,海岸
 8H,丘陵
 9L,湖畔
10R,川沿い
11U,都市部
12
13opinion
14y,賛成
15n,反対
16h,保留
17_na,無回答

 一つの列の情報と別の列の情報を空白行で区切ります。

 '_na' というのは欠損値(pythonでは NaN)に便宜的に付ける文字です。

    

 genderなどの列名はそのままでもいいかもしれませんが、
日本語の別名を割り当てるため data01_c.txt というファイルを用意しました。

gender,性別
height,身長
weight,体重
area,地域
opinion,意見

目次に戻る


3. まずは度数と割合を集計する

 人数を数え上げて、次の二つの表を作成します。

性別 度数 割合
男性 204 51.3
女性 194 48.7
全体 398 100.0
意見 度数 割合
賛成 153 38.7
反対 194 49.1
保留 48 12.2
全体 395 100.0

    

 上記の表を出力するスクリプトは下のとおり。

 1# count01.py (coding: cp932)
 2import pandas as pd
 3from prast import Prast
 4
 5dtf = pd.read_csv("data01.csv")
 6psx = Prast(dtf, "data01_c.txt", "data01_i.txt", "cp932")
 7dod = psx.count(["gender", "opinion"])
 8for key in dod.keys():
 9    print(dod[key])
10    print('')

 スクリプトの処理の流れは次のとおり。

目次に戻る


4. Prastクラスのオブジェクトを生成する

 Prastクラスのオブジェクトは Prast(……) で生成します。

Prast(dtf, columns=None, indexes=None, encoding="utf-8")
psx = Prast(dtf, "data01_c.txt", "data01_i.txt", "cp932")

 指定する引数は次の4つです。

    

 Prastクラスのメンバー変数(内部で保持される変数)は、
上の引数に対応するもので、次の4つです。

 メンバー変数の値を得るためのメソッドとして get_dtf, get_columns,
get_indexes, get_encoding を設定してあります。

 値をセットするためのメソッドとしては put_dtf, put_columns,
put_indexes, put_encoding があります。

目次に戻る


5. countメソッド

 countメソッドは、度数(人数とか個数)と、その割合(構成比:%)を算出します。

Prast.count(clist=None, asc=None, exclude=None)
dod = psx.count(["gender", "opinion"], False)
dod = psx.count(exclude="ID")

 戻り値(dod)は OrderedDict です。

 dod["gender"] に「性別」の度数と割合(DataFrame)が、
dod["opinion"] には「意見」の度数と割合が代入されます。

 引数は3つです。いずれも省略可能。

 clistを省略したときのobject型というのは分かりにくいですが、
通常は文字列型です。

 object型以外のDataTypeとしては、数値(int, floatなど)、
真偽値(True, False)、日時タイプなどがあります。

目次に戻る


6. 欠損値もカウントする

 Prastクラスに fillna(), recona() というメソッドを設けてあります。

 前者は欠損値にあたいを割り当てるもの、
 後者は特定の値を欠損値に戻すものです。

 fillna() を適用してから count() を実行すると、欠損値もカウントされます。

性別 度数 割合
男性 204 51.0
女性 194 48.5
記載なし 2 0.5
全体 400 100.0

    

Prast.fillna(clist=None, naval=None, exclude=None)
Prast.recona(clist=None, naval=None, exclude=None)
psx.fillna(["gender", "opinion"], "_na")
psx.fillna(["height", "weight"], -1)

 fillna, recona は、戻り値として DataFrame を返します。
欠損値の処理を施した後の DataFrame です。

 引数は次の3つ。

 引数を省略して psx.fillna() とすれば、
object型の列すべての欠損値が '_na' になります。

 data01.csv の場合、height, weight 以外の全列の欠損値が '_na' になります。

 サンプルスクリプトは、zip圧縮ファイル内の count02.py を参照。

目次に戻る


7. 数値データをカテゴリー化してからカウント

 data01.csvには数値データとして身長と体重があります。

 これをカテゴリー化した上で度数を算出します。

 下の表を作成。

身長区分 度数 割合
〜150 13 3.2
150〜160 106 26.5
160〜170 206 51.5
170〜180 61 15.2
180〜 6 1.5
無回答 8 2.0
全体 400 100.0

    

 Prastクラスにカテゴリー化するためのメソッド cut() を設けました。

 サンプルスクリプトは次のとおり。

 1# count03.py (coding: cp932)
 2import pandas as pd
 3from prast import Prast
 4
 5dtf = pd.read_csv("data01.csv")
 6psx = Prast(dtf, "data01_c.txt", "data01_i.txt", "cp932")
 7bins = [0, 150, 160, 170, 180, 999]
 8psx.cut("height", ["height2", u"身長区分"], bins)
 9psx.fillna()
10dod = psx.count("height2")
11for key in dod.keys():
12    print(dod[key])

    

 Prast.cut() では4つの引数を指定できます。

 3つの引数は必ず指定します。

Prast.cut(col, new_col, bins, labels=None)
bins = [0, 150, 160, 170, 180, 999]
labels = [u"150未満", u"150以上160未満", u"160以上170未満",
    u"170以上180未満", u"180以上"]
dtfx = psx.cut("height", ["height2", u"身長区分"], bins, labels)

 cut() の戻り値は DataFrame です。
カテゴリー化した新たな列を追加したDataFrameを返します。

 cut() の引数は次のとおり。

目次に戻る


8. その他

 補足事項を少々。

(1) 「割合」の小数点以下の桁数を調整(cround)

 count() で度数と割合を算出する際、小数点以下の桁数を調整できます。

psx.cround = 2
psx.count("gender")

 上のように cround に値をセットすると、小数点以下が2桁になります。

 デフォルトの値は 1 です。

 桁数の調整は、python の round() で行っています。

    

(2) カテゴリー化の際に割り当てる値

 身長をカテゴリー化して height2=身長区分の列を新設するケースを記しました。

 cut() を実行したときに height2の各カテゴリーに割り当てられる値は、
文字としての数字 1, 2, 3, 4, 5 です。数値ではありません(計算処理不可)。

 それに別名として「〜150」 「150〜160」などが割り当てられます。

 カテゴリー化で新設された列を自前で処理する場合、念頭に置いてください。

 なお、カテゴリー化する際の区切りの値 bins は配列で
[0, 150, 160, 170, 180, 999] としました。

 最初の 0 と最後の 999 は、データ中に出てこない適当な値を当てただけです。

 無限小と無限大を示す float("-inf")float("inf") でもかまいません。

    

(3) 別名定義ファイルの処理

 列の別名定義を data01_c.txt、行の別名は data01_i.txt に書きましたが、
どちらもカンマ区切りのcsvファイルです。

 これをタブ区切りにしてもかまいません。

 あるいは、ファイル名や文字列でなく OrderedDict を引き渡すのでも大丈夫です。

psx = Prast(dtf, columns_od, indexes_od)
psx.put_columns(columns_od)
psx.put_indexes(indexes_od)
## columns_od, indexes_od は OrderedDict

 どの形式で渡されたにしろ、内部では OrderedDict で保持します。

 put_columns(), put_indexes() は、受け取った引数を
上書き、または追加の形で OrderedDict に加えます。

 なお、内部で保持される OrderedDict のキーは文字型になります。

 1, 2, 3 などの数字がキーの場合、それが数値(int, float)であっても
文字(str)に変換します。

    

(4) 整数による識別の際に注意する点

 data01.csv では男性をm、女性をfにしてありますが、
男性を1、女性を2のように数字で識別する場合もあるとおもいます。

 csvファイルの中に 1, 2 が書かれていると、
pythonでは文字でなく数値になるようです。

 csvの中で "1" のように引用符で囲んでも数値になります。

 また、同じ列の中に欠損値があると、
整数値(int)でなく浮動小数点数(float)になります。
欠損値がなければint型なんですが……

gender
1,男性
2,女性

 別名割り当てファイルの中で上のように書いておけば問題ありませんが、
数値だと object型になりません。

 Prastクラスの count(), fillna(), recona() は
列ラベルを指定しないと(省略すると) object型の列すべてを対象にします。
でも、数値だとその対象になりません。

psx.fillna("gender", 0)

 欠損値に 0 を割り当てるなら、上のようにします。

 より基本的な方法として、csvファイルを読み込むときに
gender のdtypeを指定する方法もあります。

dtf = pd.read_csv("data01.csv", dtype={"gender": str})

 上のようにすると、genderの列が int, float でなく object型になります。

〜 以上 〜

Copyright (C) T. Yoshiizumi, 2018 All rights reserved.