🪐
pandasでGoogle Formのアンケート結果を集計するときよく使う操作Tips
はじめに
Google Formでアンケートを取ったときに、数値化やOne hot encodingなどの事前処理や平均、分散や相関分析を行いたいことがあると思いますが、そのときに使えるpandasのTipsです。
今回処理をするサンプルアンケートデータ
import pandas as pd
# データを辞書形式で作成
data = {
'名前': ['国崎', '岡崎', '神尾', '月宮', '古河', '能美'],
'Eメール': ['kunisaki@example.com', 'okazaki@example.net', 'kamio@example.co.jp', 'tsukimiya@example.org', 'furukawa@example.com', 'noumi@example.net'],
'年齢': [34, 28, 42, 37, 46, 51],
'満足度 (1-5)': [5, 4, 3, 2, 4, 5],
'おすすめ度 (%)': [100, 90, 80, 60, 85, 95],
'評価': ["良い", "悪い", "普通", "とても良い", "とても悪い", "普通"],
'サポートの評価': ["良い", "悪い", "", "とても良い", "", "普通"],
'購入商品': ['スマートフォン, ノートパソコン, ', 'ノートパソコン, イヤホン', 'イヤホン', 'タブレット', 'コーヒーメーカー, タブレット, ノートパソコン', ''],
'お客様の声': ['非常に満足しています。サポートも素晴らしいです。', '製品は良いですが、配送が少し遅かったです。', 'イヤホンの音質はまあまあです。もう少し低音がほしいです。', '機能は豊富ですが、使い勝手があまり良くないかもしれません。', 'このコーヒーメーカーは本当に便利です。毎朝のコーヒーが楽しみです。', '軽くて持ち運びやすく、バッテリーも長持ちします。大満足です。']
}
# DataFrameを初期化
df = pd.DataFrame(data)
# 以降dfに順次処理をかけていく
事前処理
数値の変換(0~100%を0~1の小数に変換など)
# おすすめ度をパーセンテージから数値に変換
df["おすすめ度 (%)"] = df["おすすめ度 (%)"] / 100
df
リスト、ラジオボックスなどの文字列回答を数値化
# 変換テーブル
RATING_MAPPING = {
"とても悪い": 1,
"悪い": 2,
"普通": 3,
"良い": 4,
"とても良い": 5
}
# 評価を数値に変換
df["評価"] = df["評価"].map(RATING_MAPPING)
df["サポートの評価"] = df["サポートの評価"].map(RATING_MAPPING)
df
チェックボックスなどカラム区切りの複数回答をOne Hot Encoding
# 購入商品をOne hot encodingする
df = df.join(df["購入商品"].str.get_dummies(", ").add_prefix("購入商品_"))
# 購入商品の列を削除
df = df.drop("購入商品", axis=1)
df
カンマ区切りのデータを分割し、それぞれの選択肢を列にしたうえで1,0のフラグに変更する
Name | CheckBox |
---|---|
AAA | B |
BBB | A,B |
BBB | A,B,C |
のデータを
Name | CheckBox_A | CheckBox_B | CheckBox_C |
---|---|---|---|
AAA | 0 | 1 | 0 |
BBB | 1 | 1 | 0 |
BBB | 1 | 1 | 1 |
のようなデータに変換できます。
文字列変換
# Emailのドメインを抜き出す
df["ドメイン"] = df["Eメール"].str.split("@").str.get(1)
# Eメールをすべて大文字に変換
df["Eメール"] = df["Eメール"].str.upper()
# Eメールの最初の3文字と最後の3文字を残して同じ文字数の*でマスキング
df["Eメール"] = df["Eメール"].apply(lambda x: x[:3] + "*" * (len(x) - 6) + x[-3:])
# 男性には名前にMr.を、女性にはMs.をつける
df["名前"] = df.apply(lambda row: "Mr. " + row["名前"] if row["性別"] == "男性" else "Ms. " + row["名前"], axis=1)
df
テキスト欄に特定の文字が含まれているかどうかを0,1の数値化
# お客様の声に便利という単語が含まれているかどうかを判定する
df["便利というお客様の声が含まれる"] = df["お客様の声"].str.contains("便利")
# 便利というお客様の声が含まれるのカラムをintに変換
df["便利というお客様の声が含まれる"] = df["便利というお客様の声が含まれる"].astype(int)
df
事前処理を全て適用したデータ
データを表示
"",名前,Eメール,年齢,性別,満足度 (1-5),おすすめ度 (%),評価,サポートの評価,お客様の声,イヤホン,コーヒーメーカー,スマートフォン,タブレット,ノートパソコン,ドメイン,便利という単語がお客様の声に含まれる
0,Mr. 国崎,KUN**************COM,-0.677942,男性,5,1.00,4,4.0,非常に満足しています。サポートも素晴らしいです。,0,0,1,0,1,example.com,0
1,Mr. 岡崎,OKA*************NET,-1.395763,男性,4,0.90,2,2.0,製品は良いですが、配送が少し遅かったです。,1,0,0,0,1,example.net,0
2,Ms. 神尾,KAM*************.JP,0.279153,女性,3,0.80,3,NaN,イヤホンの音質はまあまあです。もう少し低音がほしいです。,1,0,0,0,0,example.co.jp,0
3,Ms. 月宮,TSU***************ORG,-0.319032,女性,2,0.60,5,5.0,機能は豊富ですが、使い勝手があまり良くないかもしれません。,0,0,0,1,0,example.org,0
4,Ms. 古河,FUR**************COM,0.757700,女性,4,0.85,1,NaN,このコーヒーメーカーは本当に便利です。毎朝のコーヒーが楽しみです。,0,1,0,1,1,example.com,1
5,Ms. 能美,NOU***********NET,1.355884,女性,5,0.95,3,3.0,軽くて持ち運びやすく、バッテリーも長持ちします。大満足です。,0,0,0,0,0,example.net,0
分析
平均、分散、偏差などの統計値の計算
# 数値のカラムだけを抽出
df_num = df.select_dtypes(include="number")
# 各カラムの合計、平均値、中央値、最大値、90percentileを計算
statistics = df_num.agg(["sum", "mean", "median", "max", lambda x: x.quantile(0.9)])
statistics
結果
"",age,満足度 (1-5),おすすめ度 (%),評価,サポートの評価,イヤホン,コーヒーメーカー,スマートフォン,タブレット,ノートパソコン,便利という単語がお客様の声に含まれる
sum,238.000000,23.000000,5.100,18.0,14.0,2.000000,1.000000,1.000000,2.000000,3.0,1.000000
mean,39.666667,3.833333,0.850,3.0,3.5,0.333333,0.166667,0.166667,0.333333,0.5,0.166667
median,39.500000,4.000000,0.875,3.0,3.5,0.000000,0.000000,0.000000,0.000000,0.5,0.000000
max,51.000000,5.000000,1.000,5.0,5.0,1.000000,1.000000,1.000000,1.000000,1.0,1.000000
<lambda>,48.500000,5.000000,0.975,4.5,4.7,1.000000,0.500000,0.500000,1.000000,1.0,0.500000
数値カラム同士の相関値マップを計算
# 数値のカラムだけ抽出する
df_num = df.select_dtypes(include="number").fillna(0)
# カラム同士の相関係数を計算
df_num.corr()
結果
"",年齢,満足度 (1-5),おすすめ度 (%),評価,サポートの評価,イヤホン,コーヒーメーカー,スマートフォン,タブレット,ノートパソコン,便利という単語がお客様の声に含まれる
年齢,1.000000,0.177385,4.229802e-02,-0.203030,-0.293456,-4.324614e-01,3.711957e-01,-0.332122,1.698956e-01,-4.805372e-01,3.711957e-01
満足度 (1-5),0.177385,1.000000,9.677734e-01,-0.362915,-0.055216,-2.208631e-01,6.984303e-02,0.488901,-5.521576e-01,4.685213e-01,6.984303e-02
おすすめ度 (%),0.042298,0.967773,1.000000e+00,-0.400000,-0.171163,1.330206e-16,6.009258e-17,0.519615,-6.846532e-01,5.163978e-01,6.009258e-17
評価,-0.203030,-0.362915,-4.000000e-01,1.000000,0.821584,-2.738613e-01,-6.928203e-01,0.346410,0.000000e+00,-5.163978e-01,-6.928203e-01
サポートの評価,-0.293456,-0.055216,-1.711633e-01,0.821584,1.000000,-5.000000e-01,-5.533986e-01,0.395285,6.250000e-02,-1.767767e-01,-5.533986e-01
イヤホン,-0.432461,-0.220863,1.330206e-16,-0.273861,-0.500000,1.000000e+00,-3.162278e-01,-0.316228,-5.000000e-01,-3.925231e-17,-3.162278e-01
コーヒーメーカー,0.371196,0.069843,6.009258e-17,-0.692820,-0.553399,-3.162278e-01,1.000000e+00,-0.200000,6.324555e-01,4.472136e-01,1.000000e+00
スマートフォン,-0.332122,0.488901,5.196152e-01,0.346410,0.395285,-3.162278e-01,-2.000000e-01,1.000000,-3.162278e-01,4.472136e-01,-2.000000e-01
タブレット,0.169896,-0.552158,-6.846532e-01,0.000000,0.062500,-5.000000e-01,6.324555e-01,-0.316228,1.000000e+00,3.925231e-17,6.324555e-01
ノートパソコン,-0.480537,0.468521,5.163978e-01,-0.516398,-0.176777,-3.925231e-17,4.472136e-01,0.447214,3.925231e-17,1.000000e+00,4.472136e-01
便利という単語がお客様の声に含まれる,0.371196,0.069843,6.009258e-17,-0.692820,-0.553399,-3.162278e-01,1.000000e+00,-0.200000,6.324555e-01,4.472136e-01,1.000000e+00
おまけ
カラム名の変換
HEADER_MAPPING = {
"名前": "name",
"Eメール": "email",
"年齢": "age"
}
# カラム名を変更
df = df.rename(columns=HEADER_MAPPING)
df
欠損値埋めての平均計算
# サポートの評価の数値のみの平均値を計算
support_mean1 = df_num["サポートの評価"].mean()
# サポートの評価の数値のみの平均値を計算(欠損値を3で埋める)
support_mean2 = df_num["サポートの評価"].fillna(3).mean()
(support_mean1, support_mean2)
# >> (3.75, 3.5)
あとは
pandasで処理したデータをCSVで保存しSpreadSheetに再度importして、グラフ化や分析をすすめるというのをよくやります。今回のようなデータの事前処理をスプレッドシートでやろうとするとかなりめんどくさいので、ぜひpandasで行うことをおすすめします
また、上記のコードのNotebookもこちらのレポジトリに用意しています。ご自由にご利用ください。
Discussion