(旧) ヒノマルクのデータ分析ブログ

どんな企業、地域、国でも働けるようなスキルを身につけていくブログ

PythonやBIツールを使って分析と可視化をします

既婚女性の不倫傾向の集計をしてみた

このデータセットは3,4年くらい前に当時のチームメンバーにPythonでデータ分析する方法を共有したときのサンプルデータとして利用しました。

単純にロジスティック回帰でモデリングするコードを順に説明しただけなので、もう少しデータの中身を詳しく説明すべきだったなと今思えば反省点として浮かび上がります。

前回のIrisのデータセット同様、データの理解ができていないうちは集計やモデリング作業に入るべきではないですね。

自分がコンサルという立場でお客様のデータを分析する場合もデータに関する疑問は都度確認しないと分析結果が意味ないものになってしまう可能性があります。

初めて分析系の記事を投稿した感想は、

自分が今まで経験したことをブログなどの形式でまとめておくことは知識の定着や今足りないことを見つけるのに良さそうだと思いました。

技術やアルゴリズムは進化していくので、個人的にもどんどんキャッチアップしていきたいと思います。

可能であればサンプルデータの分析にも新しく学んだ知識を適用させていけたら面白いですね。

人生は一生勉強です。

1. 利用データ説明

何のデータか

今回のサンプルデータはAffairsというデータセットになります。 調べていてわかったのですが、2種類のデータセットが存在することがわかりました。

1つ目が女性向け雑誌「Redbook」の不倫に関するアンケート結果 (1974年実施)

2つ目がサイエンス系雑誌「Psychology Today」の不倫に関するアンケート結果 (1969年実施)

になります。

女性向け雑誌「Redbook」に関しては下記

en.wikipedia.org

サイエンス系雑誌「Psychology Today」に関しては下記

en.wikipedia.org

データ取得先はどこ

私が以前利用したデータは1つ目のデータセットでstatsmodelsから利用することができます。 statsmodels/datasets

両方のデータはstatsmodels/datasets内の参照元から入手可能です。

Source¶ Fair, Ray. 1978. “A Theory of Extramarital Affairs,” Journal of Political Economy, February, 45-61. The data is available at http://fairmodel.econ.yale.edu/rayfair/pdf/2011b.htm

どのようなデータか

データ数: 6,366レコード

変数一覧:

英名 日本語訳 備考
rate_marriage 結婚生活がどれくらい上手くいっているか 1とても悪い<->5とても良い
age 年齢 17.5:20以下、22:20-24、27:25-29、32:30-34、37:35-39、43:40以上
yrs_married 結婚期間 0.5:1年未満、2.5:1-4年、6:5-7年、9:8-10年、13:10年以上・長子12歳以下、16.5:10年以上・長子12-17歳、23:10年以上・長子18歳以上
children 子供の数 0、1、2、3、4、5.5:5人以上
religious 宗教心 1なし<->4とても強い
educ 学歴 9:小学校、12:高校、14:大学、16:大学卒業、17:大学院、20:大学院卒業以上
occupation (妻の)職業 1:学生、2:農業・肉体労働者、3:頭脳労働者(営業、事務、秘書)、4:熟練技能労働者(教員・看護師・アーティストなど)、5:管理・監督者、6:院卒以上の専門家
occupation_husb 夫の職業 (妻の)職業と同じ
affairs 結婚1年あたりの不倫回数

2. どのような分析ができそうか

  • 不倫する女性としない女性の差の分析 (集計ベース)

3. Checking Affairs dataset

パッケージのインポート

import seaborn as sns
import statsmodels.api as sm
import pandas as pd

データの読み込み

df = sm.datasets.fair.load_pandas().data

データの概要把握

df.describe()
Out[0]
rate_marriage age yrs_married children religious educ occupation occupation_husb affairs
count 6366.000000 6366.000000 6366.000000 6366.000000 6366.000000 6366.000000 6366.000000 6366.000000 6366.000000
mean 4.109645 29.082862 9.009425 1.396874 2.426170 14.209865 3.424128 3.850141 0.705374
std 0.961430 6.847882 7.280120 1.433471 0.878369 2.178003 0.942399 1.346435 2.203374
min 1.000000 17.500000 0.500000 0.000000 1.000000 9.000000 1.000000 1.000000 0.000000
25% 4.000000 22.000000 2.500000 0.000000 2.000000 12.000000 3.000000 3.000000 0.000000
50% 4.000000 27.000000 6.000000 1.000000 2.000000 14.000000 3.000000 4.000000 0.000000
75% 5.000000 32.000000 16.500000 2.000000 3.000000 16.000000 4.000000 5.000000 0.484848
max 5.000000 42.000000 23.000000 5.500000 4.000000 20.000000 6.000000 6.000000 57.599991

Feature Engineering

#ダミー変数を作成。カテゴリ変数を数値に変換
# TODO: ダミー変数を作成すべきか数値をそのまま利用すべきか考え中
#       今回のデータセットでは学歴や仕事は数値の大きさに意味がありそうなので
#       そのまま分析用変数として使えるのではないかと思い始めてきた。
df = pd.get_dummies(df,columns=["educ","occupation","occupation_husb"])
# 不倫経験があるかどうかのフラグを作成 0:ない、1:ある
df["affairs_flg"] = df.apply(lambda row: 1 if row.affairs > 0 else 0, axis=1)
# 子供がいるかどうかのフラグを作成 0:いない、1:いる 
df["children_flg"] = df.apply(lambda row: 1 if row.children > 0 else 0, axis=1)

データの概要把握 その2

# データフレームの5件表示
df.head(5)
Out[0]
rate_marriage age yrs_married children religious affairs educ_9.0 educ_12.0 educ_14.0 educ_16.0 educ_17.0 educ_20.0 occupation_1.0 occupation_2.0 occupation_3.0 occupation_4.0 occupation_5.0 occupation_6.0 occupation_husb_1.0 occupation_husb_2.0 occupation_husb_3.0 occupation_husb_4.0 occupation_husb_5.0 occupation_husb_6.0 affairs_flg children_flg
0 3.0 32.0 9.0 3.0 3.0 0.111111 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1
1 3.0 27.0 13.0 3.0 1.0 3.230769 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1
2 4.0 22.0 2.5 0.0 1.0 1.400000 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0
3 4.0 37.0 16.5 4.0 3.0 0.727273 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1
4 5.0 27.0 9.0 1.0 1.0 4.666666 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1
# 全体のパーセンタイル確認
df.quantile([0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.99,1])
Out[0]
rate_marriage age yrs_married children religious affairs educ_9.0 educ_12.0 educ_14.0 educ_16.0 educ_17.0 educ_20.0 occupation_1.0 occupation_2.0 occupation_3.0 occupation_4.0 occupation_5.0 occupation_6.0 occupation_husb_1.0 occupation_husb_2.0 occupation_husb_3.0 occupation_husb_4.0 occupation_husb_5.0 occupation_husb_6.0 affairs_flg children_flg
0.00 1.0 17.5 0.5 0.0 1.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.10 3.0 22.0 2.5 0.0 1.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.20 3.0 22.0 2.5 0.0 2.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.30 4.0 22.0 2.5 0.0 2.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.40 4.0 27.0 6.0 1.0 2.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.50 4.0 27.0 6.0 1.0 2.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.60 5.0 27.0 9.0 2.0 3.0 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.70 5.0 32.0 13.0 2.0 3.0 0.152174 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0
0.80 5.0 37.0 16.5 3.0 3.0 0.742424 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0
0.90 5.0 42.0 23.0 3.0 4.0 2.000000 0.0 1.0 1.0 1.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0
0.95 5.0 42.0 23.0 4.0 4.0 4.072726 0.0 1.0 1.0 1.0 1.0 1.0 0.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
0.99 5.0 42.0 23.0 5.5 4.0 7.839996 0.0 1.0 1.0 1.0 1.0 1.0 0.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.00 5.0 42.0 23.0 5.5 4.0 57.599991 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0

データの概要把握 不倫の有無と各説明変数の関係

# パーセンタイル確認
# 不倫の有無で各変数に差があるのか確認
from IPython.display import HTML
HTML(df.groupby("affairs_flg").describe().transpose().to_html())
Out[0]
affairs_flg 0 1
rate_marriage count 4313.000000 2053.000000
mean 4.329701 3.647345
std 0.821099 1.065286
min 1.000000 1.000000
25% 4.000000 3.000000
50% 5.000000 4.000000
75% 5.000000 4.000000
max 5.000000 5.000000
age count 4313.000000 2053.000000
mean 28.390679 30.537019
std 6.809684 6.699997
min 17.500000 17.500000
25% 22.000000 27.000000
50% 27.000000 27.000000
75% 32.000000 37.000000
max 42.000000 42.000000
yrs_married count 4313.000000 2053.000000
mean 7.989335 11.152460
std 7.101994 7.185216
min 0.500000 0.500000
25% 2.500000 6.000000
50% 6.000000 9.000000
75% 13.000000 16.500000
max 23.000000 23.000000
children count 4313.000000 2053.000000
mean 1.238813 1.728933
std 1.417410 1.410401
min 0.000000 0.000000
25% 0.000000 1.000000
50% 1.000000 2.000000
75% 2.000000 3.000000
max 5.500000 5.500000
religious count 4313.000000 2053.000000
mean 2.504521 2.261568
std 0.885117 0.840767
min 1.000000 1.000000
25% 2.000000 2.000000
50% 3.000000 2.000000
75% 3.000000 3.000000
max 4.000000 4.000000
affairs count 4313.000000 2053.000000
mean 0.000000 2.187243
std 0.000000 3.437478
min 0.000000 0.043478
25% 0.000000 0.521739
50% 0.000000 1.217391
75% 0.000000 2.177776
max 0.000000 57.599991
educ_9.0 count 4313.000000 2053.000000
mean 0.006260 0.010229
std 0.078882 0.100644
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
educ_12.0 count 4313.000000 2053.000000
mean 0.315558 0.352168
std 0.464791 0.477762
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 1.000000 1.000000
max 1.000000 1.000000
educ_14.0 count 4313.000000 2053.000000
mean 0.340598 0.393570
std 0.473965 0.488660
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 1.000000 1.000000
max 1.000000 1.000000
educ_16.0 count 4313.000000 2053.000000
mean 0.195687 0.132976
std 0.396775 0.339632
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
educ_17.0 count 4313.000000 2053.000000
mean 0.085787 0.068193
std 0.280082 0.252138
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
educ_20.0 count 4313.000000 2053.000000
mean 0.056109 0.042864
std 0.230160 0.202600
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_1.0 count 4313.000000 2053.000000
mean 0.007883 0.003410
std 0.088447 0.058307
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_2.0 count 4313.000000 2053.000000
mean 0.140737 0.122747
std 0.347791 0.328227
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_3.0 count 4313.000000 2053.000000
mean 0.421516 0.470044
std 0.493859 0.499223
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 1.000000 1.000000
max 1.000000 1.000000
occupation_4.0 count 4313.000000 2053.000000
mean 0.313935 0.233804
std 0.464144 0.423352
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 1.000000 0.000000
max 1.000000 1.000000
occupation_5.0 count 4313.000000 2053.000000
mean 0.099930 0.150511
std 0.299942 0.357659
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_6.0 count 4313.000000 2053.000000
mean 0.015998 0.019484
std 0.125482 0.138251
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_husb_1.0 count 4313.000000 2053.000000
mean 0.041966 0.023380
std 0.200535 0.151145
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_husb_2.0 count 4313.000000 2053.000000
mean 0.204498 0.207501
std 0.403381 0.405616
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_husb_3.0 count 4313.000000 2053.000000
mean 0.073499 0.084267
std 0.260984 0.277855
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
occupation_husb_4.0 count 4313.000000 2053.000000
mean 0.322513 0.311252
std 0.467492 0.463118
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 1.000000 1.000000
max 1.000000 1.000000
occupation_husb_5.0 count 4313.000000 2053.000000
mean 0.272896 0.293229
std 0.445499 0.455354
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 1.000000 1.000000
max 1.000000 1.000000
occupation_husb_6.0 count 4313.000000 2053.000000
mean 0.084628 0.080370
std 0.278359 0.271932
min 0.000000 0.000000
25% 0.000000 0.000000
50% 0.000000 0.000000
75% 0.000000 0.000000
max 1.000000 1.000000
children_flg count 4313.000000 2053.000000
mean 0.556689 0.755480
std 0.496834 0.429907
min 0.000000 0.000000
25% 0.000000 1.000000
50% 1.000000 1.000000
75% 1.000000 1.000000
max 1.000000 1.000000

俯瞰結果まとめ

  • 不倫している女性の方が今の結婚生活に満足していなさそう。
  • 結婚歴は不倫している女性の方が長い。因果関係はこれだけだと分からない、結婚している期間が長いほど不倫の機会が多いだけかも知れない。
  • 不倫している女性の方が子持ちの率が高い。これも因果関係は分からない。
  • 女性の学歴と不倫の有無はあまり関係なさそう
  • 管理・監督系の仕事をしている女性は不倫率が若干高そう
  • 夫の職業は不倫の有無にあまり関係なさそう

図にして視覚的に確認する

# 描画設定
from matplotlib import rcParams
rcParams['xtick.labelsize'] = 12       # x軸のラベルのフォントサイズ
rcParams['ytick.labelsize'] = 12       # y軸のラベルのフォントサイズ
rcParams['figure.figsize'] = 18,8      # 画像サイズの変更(inch)

相関係数確認

# df.describe()で確認した結果が表されている
sns.heatmap(round(df.corr(),1), vmax=1, vmin=-1, center=0,annot=True)
Out[0]

各変数と不倫の有無をボックスプロットで確認

# 結婚満足度と不倫の有無をボックスプロットで確認
# 不倫していない方が不倫している女性より満足度が高い
sns.boxenplot(x="affairs_flg",y="rate_marriage",data=df)
Out[0]

# 年齢と不倫の有無をボックスプロットで確認
# 不倫している女性の方が年齢が高い
# ただし、年齢高い = 結婚歴長い = 不倫の機会が多い という関係がある可能性もある
sns.boxenplot(x="affairs_flg",y="age",data=df)
Out[0]

# 結婚歴と不倫の有無をボックスプロットで確認
# 不倫している女性の方が結婚歴が長い
# ただし、結婚歴長い = 不倫の機会が多い という関係がある可能性もある
sns.boxenplot(x="affairs_flg",y="yrs_married",data=df)
Out[0]

# 子供の数と不倫の有無をボックスプロットで確認
# 不倫している女性の方が子供が多い
# ただし、子供が多い = 結婚歴が長い =  不倫の機会が多い という関係がある可能性もある
sns.boxenplot(x="affairs_flg",y="children",data=df)
Out[0]

# 信仰心と不倫の有無をボックスプロットで確認
# 不倫している女性の方が全体的に低めに見えるが、差があるとは言いづらい
sns.boxenplot(x="affairs_flg",y="religious",data=df)
Out[0]

Open In Colab

4. check package versions

分析環境はanaconda macosです。 conda --version の結果はconda 4.7.12です。

今回利用したパッケージのバージョンは下記です。

import seaborn
import matplotlib
import sklearn
import pandas
import numpy
print("seaborn:"+ seaborn.__version__)
print("matplotlib:"+ matplotlib.__version__)
print("sklearn:"+ sklearn.__version__)
print("pandas:"+ pandas.__version__)
print("numpy:"+ numpy.__version__)
Out[0]
seaborn:0.9.0
matplotlib:3.1.0
sklearn:0.21.2
pandas:0.24.2
numpy:1.16.4

5. 考察