pythonのprastクラスによる集計(6) 複数回答の集計・前篇

2018/11/04

 今回は複数回答の集計を取り上げます。

 主な情報源として「新聞、雑誌、テレビ、ラジオ」の4つの選択肢を示し、
該当するものに○をつけてもらうケースです。

 どの選択肢にどれくらい「○」がついているか、その度数や割合をみます。

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

    


《このページの目次》


    

1. 処理するcsvファイル

 zip圧縮ファイルに入っている data01.csv は
「ID, group, newspaper, magazine, tv, ragio」 の6列からなる
98行(98人分)のデータです。

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

 groupの列は年齢層で、20, 30, 40 の整数値が書かれています。

 20歳代、30歳代、40歳代の意味です。

 newspaperなどの複数回答の列は、○の代わりに数値の 1 が記入されています。

 選択されなかったところは空欄。

 csvを表形式で示すと下のとおり(空欄は□)。

ID group newspaper magazine tv ragio
1 30 1
2 40 1 1
3 40 1
4 20 1

    

 もう一つ data01single.csv というのもあります。

 複数回答を1つの列におさめる書き方です。

 resourceという列には「newspaper,ragio」のような文字が記入されています。

 ○がつけられた選択肢をカンマで区切って列挙する形式。

 具体的には下のとおり。

ID group resource
1 30 newspaper
2 40 newspaper,magazine
3 40 magazine
4 20 tv
5 30 newspaper,magazine,ragio

 この形式だと処理しにくいので前述の data01.csv の形式に変換し、
その上で集計します。

目次に戻る


2. 各選択肢の度数と割合を集計するma_count

 全部で98人いる中で、新聞を選択した人が何人いるかなどをみます。

 これは ma_count() で行います。

(1) 作成する表

 まず作成する表を掲げておきます。

  度数 割合
新聞 52 53.1
雑誌 56 57.1
テレビ 46 46.9
ラジオ 38 38.8
総数 98 100.0

 総数98人のうち、新聞を選んだ人が52人(53.1%)、
雑誌に○をつけた人が56人(57.1%)いたことを意味します。

    

(2) スクリプト

 前述の表を出力するスクリプトは次のとおり。

 1# ma_count.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")
 7rng = ['newspaper', 'magazine', 'tv', 'ragio']
 8dod = psx.ma_count(rng, asc=None, value=1)
 9print(dod[0])

    

(3) ma_countメソッド

 Prastクラスの ma_count() は、前述のような集計表を出力するものです。

 ma は multi-anser のつもり。

dod = ma_count(clist, asc=None, value=1)

 戻り値の dod は OrderedDict ですが、その key は 0 から始まる整数値です。

 dod[0] に度数と割合の DataFrame が代入されています。

 第1引数の clist の指定の仕方によって dod[1] などが
セットされるかもしれません。

 引数の意味は次のとおり。

    

 複数回答の列ラベルをリストで指定するのは少々面倒です。

 そこで、crange() を設けてあります。

 下のいずれも ['newspaper', 'magazine', 'tv', 'ragio'] を返します。

rng = psx.crange('newspaper', 'ragio')  # 最初と最後の列ラベルを指定
rng = psx.crange(2, 5)  # 最初と最後の列番号を指定(始点は 0)
rng = psx.crange('ragio', 'newspaper')  # 順番を入れ替えても同じ
rng = psx.crange(5, 2)  # 同上

 行の一定範囲を指定するための irange() もあります。

 使い方は crange() と同じです。

目次に戻る


3. 単一列・列記方式の変換

 複数回答を記録するやり方として、
一つの列に○の付いた選択肢を詰め込む方法があります。

resourceという一つの列に「newspaper,tv」とか「magazine,ragio」のように
○の付いた選択肢を列記する方式です。

 zip圧縮ファイルに入っている data01single.csv は、
この単一列・列記方式で書かれています。

 この形式は扱いにくいので、data01.csv のような
複数列・マーク付け方式に変換してから処理します。

(1) single2multiメソッド

 単一列・列記方式の DataFrame を複数列・マーク付け方式に変換するのが
single2multi() というメソッドです。

 これは Prastクラスには属していません。

single2multi(dtf, mcolumn, sep=",", value=1, na=NaN, clist=None)
new_dtf = single2multi(dtf, "resource", sep=",",
    value=1, na=0, clist=None)

 戻り値の new_dtf には複数回答の新しい列が追加されています。
単一列の resource も残っているので、不必要なら自前で消去します。

 引数の意味は次のとおり。

 最後の引数 clist が None であれば、選択肢の順番は「お任せ」になります。

 「ragio, newspaper, tv, magazine」のような順番になるかもしれません。

 「お任せ」にしたくない場合は下のようにします。

single2multi(……, clist=['newspaper', 'magazine', 'tv', 'ragio'])

 なお、単一列を分解して新設された列の名前(list)は、
グローバル変数のmrange にセットされます。

    

(2) スクリプト

 single2multi() で変換してから ma_count() を用いるサンプルを掲げます。

 1# ma_single2multi.py (coding: cp932)
 2import pandas as pd
 3import prast as ps
 4ps.set_encoding("cp932")  # python2用にencodingを設定
 5
 6dtf = pd.read_csv("data01single.csv")
 7dtf = ps.single2multi(dtf, "resource", sep=",", value=1, na=0,
 8    clist=['newspaper', 'magazine', 'tv', 'ragio'])
 9rng = ps.mrange  # resourceを分解してできた新設の列の名前
10dtf.drop("resource", axis=1, inplace=True)  # resourceという列を消去
11dtf.to_csv("data02.csv", index=False)  # 念のためcsvとして書き出す
12psx = ps.Prast(dtf, "data01_c.txt", "data01_i.txt", "cp932")
13dod = psx.ma_count(rng, asc=None, value=1)
14print(dod[0])

    

(3) 逆の変換を行う multi2singleメソッド

 single2multi() と逆の変換を行う multi2single() もあります。

 複数列・マーク付け方式→単一列・列記方式の変換です。

multi2single(dtf, clist, mcolumn, sep=",", value=1)
new_dtf = multi2single(dtf, ['newspaper', 'magazine', 'tv', 'ragio'],
    "resource", sep=",", value=1)

 参考までサンプルスクリプトを掲げておきます。

 1# ma_multi2single.py (coding: cp932)
 2import pandas as pd
 3import prast as ps
 4ps.set_encoding("cp932")  # python2用にencodingを設定
 5
 6dtf = pd.read_csv("data01.csv")
 7rng = ['newspaper', 'magazine', 'tv', 'ragio']
 8dtf = ps.multi2single(dtf, rng, "resource", sep=",", value=1)
 9dtf.drop(rng, axis=1, inplace=True)  # 複数回答の複数列を消去
10dtf.to_csv("data03.csv", index=False)  # csvとして書き出す

目次に戻る


4. 単一回答と複数回答の組み合わせ集計

 data01.csv には group(年齢層)があります。単一回答です。

 各年齢層ごとに複数回答がどれくらい選ばれているかを集計するため
ma_table() を設けてあります。

(1) 作成する表

 ma_table() は、戻り値として4つの DataFrame を返します。

 そのうちの1つは次のとおり。

  新聞 雑誌 テレビ ラジオ 総数
20代 13 17 12 12 31
30代 24 17 18 12 34
40代 15 22 16 14 33
合計 52 56 46 38 98

 最右列の「総数」は、合計値ではなく、各年齢層の人数の総数です。

 一方、最下行の「合計」は素直な合計値です。

 年齢層が単一回答なので合計値が意味を持ちます。

    

 得られる4つの表は次のとおり。

 pct1の表は下のようになります。

  新聞 雑誌 テレビ ラジオ 総数
20代 41.9 54.8 38.7 38.7 100.0
30代 70.6 50.0 52.9 35.3 100.0
40代 45.5 66.7 48.5 42.4 100.0
合計 53.1 57.1 46.9 38.8 100.0

    

(2) スクリプト

 単一回答と複数回答の組み合わせ集計を行うスクリプトは次のとおり。

 1# ma_table.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")
 7rng = psx.crange(2, 5)
 8dod = psx.ma_table(rng, "group", value=1)
 9for key in dod.keys():
10    print(key)
11    print(dod[key])
12    print('')

    

(3) ma_tableメソッド

 ma_table() は、第1引数に複数回答の列ラベル群、
第2引数に単一回答の列ラベルを指定します。

dod = ma_table(rng, gkey, value=1)

 第3引数の value は、複数回答の列において
選択されたものに割り当てられている値(デフォルトは数値の1)です。

 集計の際、この value の値のセルをカウントします。

 戻り値は OrderedDict で、4つのDataFrame がセットされています。

 これについては4つの表(度数と割合の表)として既に書いたので省略。

目次に戻る


5. 選択肢相互の関連の度合い

 「新聞」に○をつけた人のうち、どれくらいの人が「雑誌」にも○をつけたかなどをみます。

 それを行うため ma_rel() というメソッドを設けました。

(1) 作成する表

 下のような表を作成します。

 列と行のどちらも、複数回答の選択肢の名前です。

  新聞 雑誌 テレビ ラジオ
新聞 52 29 21 23
雑誌 29 56 26 20
テレビ 21 26 46 9
ラジオ 23 20 9 38

 1行目は「新聞」に○をつけた人の情報です。

 「1行・2列」のセルは「新聞」と「雑誌」の交差点ですが、
「新聞」と「雑誌」の両方に○をつけた人数を示します。

 「1行・1列」は「新聞」と「新聞」の交差点で、
要するに「新聞」を選んだ人の総数を意味します。

 この表は、行と列を入れ替えても同じものになります。

 この度数の表についてパーセンテージを示す場合、2種類が考えられます。

 1行目は「新聞」を選んだ人を100%とする割合、
 2行目は「雑誌」に○をつけた人を100%とする割合…… とする表。

 もう一つは、回答者総数(98人)を100%とする表です。

 ma_rel() は、度数表と2つのパーセンテージの表(合計3つ)を作成します。

 参考まで、下に2種類のパーセンテージの表を掲げておきます。

  新聞 雑誌 テレビ ラジオ
新聞 100.0 55.8 40.4 44.2
雑誌 51.8 100.0 46.4 35.7
テレビ 45.7 56.5 100.0 19.6
ラジオ 60.5 52.6 23.7 100.0

 上記は、左上から右下の対角線がいずれも 100%になっています。

N=98 新聞 雑誌 テレビ ラジオ
新聞 53.1 29.6 21.4 23.5
雑誌 29.6 57.1 26.5 20.4
テレビ 21.4 26.5 46.9 9.2
ラジオ 23.5 20.4 9.2 38.8

 左上端の「N=98」が、分母になった「総数」を示します。

    

(2) スクリプト

 ma_rel() を用いるサンプルスクリプトを掲げます。

 1# ma_rel.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")
 7rng = psx.crange(2, 5)
 8dod = psx.ma_rel(rng, value=1)
 9for key in dod.keys():
10    print(key)
11    print(dod[key])
12    print('')

    

(3) ma_relメソッド

 ma_rel() の引数は2つです。

dod = ma_rel(clist, value=1)

 第1引数は列ラベルのリスト(複数回答の列ラベル)。

 第2引数は○に与えられている値です。

 戻り値は OrderedDict で、3つの DataFrame がセットされています。

 今回はこれで終了です。

 次回、複数回答の簡単な分析について書きたいとおもいます。

〜 以上 〜

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