見出し画像

PythonでKickstarterの成功/失敗分析

財布とかカバンとかの日用品をkickstarterで買うことにハマっておりまして。

こんな分析をしてみました。

このnoteのやり方編です。

こんな方におすすめ

マーケターでプログラミングを勉強中の方
エクセルではできない分析手法に興味がある方
・Pythonを学習し始めて、基礎的なことは理解して、何か面白いことできないかな〜と思っておられる方

このnoteできるようになること

このあとは、下記のようなアウトプットを出してます。

決定木_2

pandas-profiling、plotly、seaborn、scikit-learnを使用してます。

また、イカしたアウトプットのためにdtreeviz、textblob、wordcloudというライブラリも使ってます。これが使えると会社で少しイキれるかもです。

準備

分析環境の準備

まず、分析環境の構築です。分析には、「Jupyter Notebook」を使います。コチラのnoteの前半の無料部分の「◆まず、環境の準備です」をご覧ください。

ライブラリのimport

#使用するライブラリのimport
import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns

#seaborn色の設定
sns.set_style('whitegrid')

#matplotlibをインライン表示
%matplotlib inline

データの取り込み

#データの取りこみ
df = pd.read_csv("/[あなたのディレクトリ]/ks-projects-201801.csv")

#行と列の確認
df.shape
#head確認
df.head()

欠損値の確認

#欠損値の確認と前処理
import missingno as msno

#描画して欠損値を視覚的に確認
check_MV = msno.nullity_sort(df)
msno.matrix(check_MV, color=(0.10, 0.10, 0.80))

前処理

欠損値は少ないデータでした。usd pledged列が欠損してそうです。

#欠損値の集計
df.isnull().sum()

欠損値2

name列の一部、欠損値がありました。それぞれ処理します。

#nameの欠損値はunknownに
df.name =df.name.fillna("unknown")

#usd pledgedは使わないので削除
df = df.drop(["usd pledged"],axis=1)

データの前処理

#日付データの列をto_datetimeする
df.deadline = pd.to_datetime(df.deadline)
df.launched = pd.to_datetime(df.launched)

#launchedを日付だけにする
df.launched= df.launched.dt.date

#launchedのデータを日付だけにしたことで、objectに戻っちゃったので、再度to_datetimeする
df.launched = pd.to_datetime(df.launched)​

前菜🥗データの概観

こちらの書籍で紹介されていた、こちらのライブラリを使います。超絶便利です。

ライブラリのinstall

anaconda_ライブラリのインストール

↑でターミナルを開き、下記を実行してインストールします。

pip install pandas-profiling

インストールが上手くいかない時はこの辺りをお試しください。

無事インストールが済んだら、Jupyter Notebookに戻って下記でimport。

#EDA用のライブラリをimport
import pandas_profiling as pp

プロファイルを作る

profile = pp.ProfileReport(df, title = "ks-projects-201801")

#出力
profile.to_notebook_iframe()

こんな感じで作成が進み、

作成中

出力されます。

タブを切り替えたりと色々、心ゆくまでデータを探索ください。

画像20

html形式で保存もできます。

profile.to_file("ks-projects-2018.html")

募集期間のデータ列を追加する

#deadlineとlaunchedの差分を出す
df["period"] = df.deadline - df.launched

#timedeltaの型からintに変換しておく
df["period_int"] = df.period.dt.days

#確認
df["period_int"].describe()​

period_int_外れ値

すごい外れ値があるので処理します。

外れ値の処理

#外れ値の境界を決める
q1 = df["period_int"].quantile(q = 0.25)
#q2 = df["period_int"].quantile(q = 0.50)
q3 = df["period_int"].quantile(q = 0.75)

#第1四分位値 と 第3四分位値 の範囲
iqr = q3 - q1

#上限
upper = q3 + 1.5*iqr
#下限
lower = q1 - 1.5*iqr

#外れ値を外したdfを用意する
df_iqr = df[(df["period_int"] >= lower) & (df["period_int"] <= upper)].dropna()

#確認
df_iqr["period_int"].describe()

外れ値処理ご

参考:データに含まれる異常値を検出してみよう

箱ひげ図を作る

#箱ひげ図にしてみる
plt.figure(figsize=(10, 10))
sns.boxplot(y="period_int", data = df_iqr)

画像8

ヒストグラムを作る

#ヒストグラムの最適な山の数を計算
import math
sturges = lambda n: math.ceil(math.log2(n*2))

bins = sturges(len(df_iqr['period_int'])) 

#ヒストグラムにする
plt.figure(figsize=(20,10))
sns.histplot(df_iqr.period_int, kde = True, bins = bins)

ヒストグラム

参考:Pythonのseabornで手軽なのに美しいヒストグラムを作成する方法

箱ひげ図を作る2:usd_goal_real

#外れ値の境界を決める
q1_g = df["usd_goal_real"].quantile(q = 0.25)
q2_g = df["usd_goal_real"].quantile(q = 0.50)
q3_g = df["usd_goal_real"].quantile(q = 0.75)

iqr_g = q3_g - q1_g

upper_g = q3_g + 1.5*iqr_g
lower_g = q1_g - 1.5*iqr_g

#外れ値を外したdfを用意する
df_iqr_goal = df[(df["usd_goal_real"] >= lower_g) & (df["usd_goal_real"] <= upper_g)]
#箱ひげ図にしてみる
plt.figure(figsize=(10, 10))
sns.boxplot(y="usd_goal_real", data = df_iqr_goal)

箱髭図2

ヒストグラムを作る2:usd_goal_real

#import math
sturges = lambda n: math.ceil(math.log2(n*2))

bins_goal = sturges(len(df_iqr_goal['usd_goal_real'])) 

#描画
plt.figure(figsize=(20,10))
sns.histplot(df_iqr_goal["usd_goal_real"], kde = True, bins = bins_goal)

ヒストグラム2

項目別の箱ひげ図

#pxのimport
import plotly.express as px

#重そうなのでサンプル抽出する
n = 20000

#集計軸
axis = "main_category" #任意の項目の列名をいれる
box = "period_int"#図示したい列を入れる

#箱ひげ図の生成
fig = px.box(
   df_iqr.sample(n=n),
   x=axis,
   y=box
)

#描画
fig.update_layout(
   title=str(axis)+"別の募集期間(n="+ str(n) +")",
   width=1500,
   height=500,
   plot_bgcolor="rgba(0,0,255,0.1)" #お好みで背景色の調整
)

#描画
fig.show()

画像12

main_category別の募集期間件数と平均

ライブラリのimport

#plotlyで描画してみる
import plotly.graph_objects as go
from plotly.subplots import make_subplots

描画のための準備

#集計軸と集計項目をセット
axis_2 = "main_category"
values = "period_int"

#groupby
df_iqr_GB_mean =  df_iqr.groupby(axis_2).mean()

#plotly用にlistを用意
df_iqr_GB_mean_list = df_iqr_GB_mean[values].values.tolist()
df_iqr_GB_unique = df_iqr_GB_mean[values].index.tolist()

#plotly用にlistを用意
df_iqr_GB_count = df_iqr.groupby(axis_2).count()
df_iqr_GB_count_list = df_iqr_GB_count[values].values.tolist()

#plotly用にlistを用意
title = axis_2 + "別の募集期間平均"
labels = list(df_iqr_GB_mean[values].sort_values().index)
line_size = list(round(df_iqr_GB_mean[values].sort_values(),2))
line_text =  list(round(df_iqr_GB_mean[values].sort_values(),2))

グラフの描画

#描画エリアの用意
fig = make_subplots(
   rows=1,
   cols=2,
   specs=[[{}, {}]],
   shared_xaxes=True,
   shared_yaxes=False, vertical_spacing=1
)

#グラフの追加
fig.append_trace(
   go.Bar(
       x=df_iqr_GB_count_list,
       y=df_iqr_GB_unique,
       marker=dict(
           color='rgba(0,0,255,0.5)',
           line=dict(
               color='rgba(0,0,255,0.5)',
               width=1),
       ),
       name='count',
       orientation='h'
), 1, 1)

fig.append_trace(
   go.Scatter(
       x=df_iqr_GB_mean_list,
       y=df_iqr_GB_unique,
       marker=dict(
           color='rgb(0,0,255,0.5)',
           line=dict(
               color='rgba(0,0,255,0.5)',
               width=1),
       ),
       name='ave.',
       orientation='h',
), 1, 2)


#グラフタイトルの追加
fig.update_layout(
   title=axis_2 + "別" + values + "の件数と募集期間Ave.",
   plot_bgcolor="rgba(0,0,255,0.1)"
)

#描画
fig.show()

画像13

メイン🥩成功/失敗分析

①successとfailのクロス集計

#goalの外れ値を外したdfの中で、stateの成功と失敗だけ抽出する
df_SandF  = df_iqr_goal [(df_iqr_goal["state"] == "successful") | (df_iqr_goal["state"] == "failed")]
#集計軸となる列名をセット
analysis_axis = "main_category" #currency, main_category, category or country

#セットした集計軸でgroupbyする
count_SandF = pd.pivot_table(df_SandF,index= analysis_axis, columns="state",values="ID", aggfunc="count")

#成功、失敗、その他の合計を出す
SandF_sum = count_SandF.successful + count_SandF.failed
count_SandF["ttl"] = df_iqr_goal.groupby(by=analysis_axis).size()
count_SandF["others"]= count_SandF.ttl - (count_SandF.successful +count_SandF.failed)

#順番を整える
count_SandF = count_SandF.reindex(columns=["successful","failed","others","ttl"])

#別のdfを用意して、構成比を計算する
ratio_SandF = pd.DataFrame()

ratio_SandF["successful_%"] = round(count_SandF.successful / count_SandF.ttl *100, 2)
ratio_SandF["failed_%"] = round(count_SandF.failed / count_SandF.ttl *100, 2)
ratio_SandF["others_%"] = round(count_SandF.others / count_SandF.ttl *100, 2)
ratio_SandF["ttl_%"] = round(count_SandF.ttl / count_SandF.ttl *100, 2)

#成功の割合で降順にする
ratio_SandF.sort_values("successful_%", inplace = True)
#plotly用のlistを用意
success = list(ratio_SandF["successful_%"])
failed = list(ratio_SandF["failed_%"])
others = list(ratio_SandF["others_%"])
names = list(ratio_SandF.columns)
labels = list(ratio_SandF.index)

#グラフの用意
fig = go.Figure(data=[
   go.Bar(
       name='success',
       y=labels,
       x=success,
       orientation="h",
       marker=dict(
           color = 'rgba(255, 0, 127, 0.5)',
           line =dict(color= 'rgba(255, 0, 127, 1)',
                      width=3)
       )
   ),
   go.Bar(
       name='failed',
       y=labels,
       x=failed,
       orientation="h",
       marker=dict(
           color = 'rgba(58, 0, 183, 0.1)',
           line =dict(color='rgba(58, 0, 183, 1)',
                     width=1)
       )
   ),
   go.Bar(
       name='other',
       y=labels,
       x=others,
       orientation="h",
       marker=dict(
           color =  'rgba(64, 68, 77, 0.1)',
           line =dict(color='rgba(64, 68, 77, 1)',
                     width=1)
       )  
   ),
])

#表示用件
fig.update_layout(title = str(analysis_axis)+"別の構成比",
                 barmode='stack',
                 plot_bgcolor="rgba(0,0,0,0)",
                 width=900, height=len(df_SandF[analysis_axis].unique())*40
               )

#描画
fig.show()

画像21

analysis_axis = に列名を指定するとクロス集計ができます(カテゴリ変数のみ)。

②ロジスティック回帰と決定木

ロジスティック回帰_ライブラリのimport

#まず、ロジスティック回帰をする
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression as LR

データの準備

#レコード数が多いのでサンプルを抽出する
sample = 10000

#sampleの件数分を元のdfからランダム抽出する
df_SandF_sample = df_SandF.sample(n=sample).sort_index()

#確認
print(df_SandF_sample.shape)

データが多いのでサンプル抽出する

#レコード数が多いのでサンプルを抽出する
sample = 10000

#sampleの件数分を元のdfからランダム抽出する
df_SandF_sample = df_SandF.sample(n=sample).sort_index()

#確認
print(df_SandF_sample.shape)

目的変数を用意する

#successfulを1、failedを0にして目的変数にする
Y_LogReg = df_SandF_sample["state"].map(
   {
       "successful":1,
       "failed":0
   }
)

#確認
Y_LogReg.head()

説明変数を用意する

#説明変数にならなそうな変数の列を削除
df_LogReg = df_SandF_sample.drop(columns = [
   "ID",
   "name",
   "deadline",
   "launched",
   "pledged",
   "state",
   "period",
   "backers",
   "usd_pledged_real",
   "usd_goal_real"
])

#確認
df_LogReg.head()

データの確認

分析実施

#ロジスティクス回帰用にカテゴリ変数をダミー化する
X_LogReg = pd.get_dummies(df_LogReg)

#モデル作成用にデータを分割する
X_train, X_test, y_train, y_test = train_test_split(
   X_LogReg,
   Y_LogReg,
   test_size=0.2,
   random_state=1234
)

#インスタンスの作成
clf_LogReg = LR() 

#学習
clf_LogReg.fit(X_train, y_train) 

#モデルの評価
print("教師データを使った正解率")
print(clf_LogReg.score(X_train, y_train))
print("テストデータを使った正解率")
print(clf_LogReg.score(X_test, y_test))

モデルの評価

係数の確認

#モデルの係数を表示する
coeff_LogReg = pd.DataFrame([X_LogReg.columns, clf_LogReg.coef_[0]]).T

#列名を整える
coeff_LogReg.rename(columns={0:"variable",1:"coefficient"}, inplace = True)

#係数の大きさ順に並び替え_top10
coeff_LogReg.sort_values(by = "coefficient", ascending= False).head(10)

ロジスティック回帰_結果

決定木_ライブラリのimport

#決定木のライブラリをimport
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import DecisionTreeRegressor

#決定木の可視化用のライブラリをimport
from dtreeviz.trees import dtreeviz
import pydotplus
from IPython.display import Image
from graphviz import Digraph

以下、有料にしてますが返金可能なのでお気軽にお楽しみください^-^

ここから先は

9,570字 / 12画像
この記事のみ ¥ 250

貴重なお時間で読んでいただいてありがとうございます。 感謝の気持ちで、いっPython💕