想定状況
想定している状況はこんな感じです:
いま、Table 1 のようなアンケート集計結果表があったとします。
回答(answer column)はいわゆるリッカート尺度(Likert scales)で、質問票では「1. 非常にそう思う, 2. そう思う, 3. どちらでもない, 4. あまりそう思わない, 5. 全くそう思わない」の 5 段階尺度となっていました。けれど、なんかの拍子に数字が抜け落ちちゃった。
でもこれだと見難いので、やはり尺度の 1-5 と一致するように、DataFrame を任意の順序でソート したい。
Excel であれば、並べ替え > ユーザー設定リスト で解決する場面ですが、Pandas ではどうすれば良いか?という状況です。
Table 1: アンケート結果表
respondent_id | answer |
id_02 | 無回答 |
id_15 | どちらでもない |
id_16 | 非常にそう思う |
id_06 | そう思う |
id_13 | どちらでもない |
id_04 | 非常にそう思う |
id_14 | そう思う |
id_07 | 無回答 |
id_10 | どちらでもない |
id_11 | そう思う |
id_05 | 無回答 |
id_01 | 無回答 |
id_03 | あまりそう思わない |
id_12 | そう思う |
id_08 | 全くそう思わない |
id_09 | あまりそう思わない |
方法
全体の流れ
全体の流れはこんな感じです。
- データを Pandas DataFrame (以下「DF」)に読み込む(Script 1)
- 任意の sort order を定義する。
今回の例では、sort_order = ['非常にそう思う', 'そう思う', 'どちらでもない', 'あまりそう思わない', '全くそう思わない', '無回答']
としました。(Script 6) - Sort 対象の column の data type を categorical に指定するとともに、categories を指定する。
今回の例では、df.answer = pd.Categorical(df.answer, categories = sort_order
です。(Script 7) - あとは普通に sort_value() する。
今回の例では、df.sort_values(by=['answer'])
です。(Script 9)
Pandas の document [1] の説明だと、第1引数と第2引数の関係が私にはちょっとわかりにくかったので、以下では冗長になりますが、クドめに説明しました 😃
DataFrame の準備
実際には CSV などから読み込むことになると思いますが、ここでは Script 1 で代用します。
Script 1
import pandas as pd # 集計表を Pandas DataFrame に読み込む。 df = pd.DataFrame( { 'respondent_id': ['id_02','id_15','id_16','id_06','id_13','id_04','id_14','id_07', 'id_10','id_11','id_05','id_01','id_03','id_12','id_08','id_09'], 'answer': ['無回答', 'どちらでもない', '非常にそう思う', 'そう思う', 'どちらでもない', '非常にそう思う', 'そう思う', '無回答', 'どちらでもない', 'そう思う', '無回答', '無回答', 'あまりそう思わない', 'そう思う', '全くそう思わない', 'あまりそう思わない'] } )
Categorical data / ソーティングと順序 の準備
任意の順序での sort(custom sort)で颯爽と登場するのが、ML でお馴染みの data type、カテゴリーデータ(Categorical data)です[2]。
まずは pandas.Categorical() method を使って、DF の column ないし Series を Categorical に変換します。
- Pandas の良いところは、並べ替えの基準にしたい column の data type を category に指定すると、その column は単なるデータの連なりではなく、何らかの分類カテゴリーが入っている column だと認識されて、カテゴリー項目分けまでしてくれるところです(親切☺️)。
- 例えば、「DF の ‘answer’ column に入っているデータはカテゴリー項目ですよ!」と Pandas に教えてあげるにはこんな感じで 👇
Script 2
df.answer = pd.Categorical(df.answer)
- この段階で Pandas にちゃんと話が通じているかどうか確認するために、answer column (series)を見てみると、Script 3 のようになります。
- 注目箇所は、
Categories (6, object): [あまりそう思わない, そう思う, どちらでもない, 全くそう思わない, 無回答, 非常にそう思う]
のところです。16 rows あった項目データから、’無回答’を含む 6 カテゴリーをちゃんと抽出しています。
でも、なんか順序がおかしいかも 😟
Script 3
df.answer
- そこで、
cat.as_ordared()
を使って、カテゴリーの順序がどう認識されていのかを確認します。
Script 4
df.answer.cat.as_ordered()
- これはつらいですね。文字列の sort 順になっていて、尺度の順序になっていないじゃん。
- と、一言文句を云う前に、そもそも順序を指定したかと自問したら、してなかった。
- 今度は順序もちゃんと指定して、仕切り直し。
Script 5
# DataFrame の読み込み直し。今回は必要ありませんが、念のための習慣。 df = pd.DataFrame( { 'respondent_id': ['id_02','id_15','id_16','id_06','id_13','id_04','id_14','id_07', 'id_10','id_11','id_05','id_01','id_03','id_12','id_08','id_09'], 'answer': ['無回答', 'どちらでもない', '非常にそう思う', 'そう思う', 'どちらでもない', '非常にそう思う', 'そう思う', '無回答', 'どちらでもない', 'そう思う', '無回答', '無回答', 'あまりそう思わない', 'そう思う', '全くそう思わない', 'あまりそう思わない'] } )
Script 6
# sort_order = [] でカテゴリーの順序をあらかじめ list にしておきます。 # もちろん、list にせず直接書いても OK です。 sort_order = ['非常にそう思う', 'そう思う', 'どちらでもない', 'あまりそう思わない', '全くそう思わない', '無回答']
Script 7
# 対象 column ヨシ! categories 指定 ヨシ! 😺 df.answer = pd.Categorical(df.answer, categories = sort_order)
Script 8
# どうかな? df.answer.cat.as_ordered()
- 大丈夫みたいですね。これで DF の answer column は、順序を定義されたカテゴリーデータが入った column と認識されました。
- なので、これ以降は answer column に普通に sort を掛けても、文字列ソートではなくて、定義した順序でソートされます。
Script 9
# 準備が整ったので、 DF に sort を掛けます。 df.sort_values(by=['answer'])
- おお、出来たできた!🍻
- ここまで来ると少し欲が出てきて、「うーん、回答者 ID がバラバラで気持ち悪いなぁ…」などと贅沢を言い出す人が必ず出てくるので、快く対応。(Script 10)
- 「うーん、回答者 ID は昇順(ascending)でいいけど、回答は降順の方が良くない?」
- そうかなぁ?と思いつつも、快く対応(Script 11)
- なお、reset_index() はお好みで。
- めでたし、めでたし 🍣🍺
Script 10
df.sort_values(by=['answer', 'respondent_id'])
(*1) なお、by=[‘respondent_id’, ‘answer’] の順序にすると、respondent_id, answer の順でソートされます。
Script 11
df.sort_values( by =['answer', 'respondent_id'], ascending=[False, True] )
(*2) by の中身の順序と ascending の中身の順序は対応しています。つまり、 by=[‘answer‘, ‘respondent_id‘] と ascending=[False, True] が対応しています。
なお、Script 11 では説明のため余分な space がいっぱい入れてありますが、ルール的にはよろしくないので本番では適当に削除し方がよいかも(このままでも動きますが😅)。
References
[1] pandas User Guide. Sorting and order ( Google翻訳 | Bing翻訳 )
[2] pandas User Guide. Categorical data ( Google翻訳 | Bing翻訳 )