ChatGPTのCodeInterpreterで統計分析をする課題
ChatGPT Plus の Code Interpreter は ChatGPT から直接 Python のプログラムを実行できます。利点として、
- 思いついたことをすぐに実験・結果が確認できる
- ファイルがアップロードできる
- Pythonからの出力をLLM自身がデバッグできる
- コードの解説や不足する情報が ChatGPT から得られる
- データがダウンロードできる
- 基本的なライブラリがインストールされている
というものがあります。一方で、
- ネットワークにアクセスできない
- パッケージのインストールが自由にできない(面倒)
- Web API や公開情報にアクセスできない
- ライブラリのインストールが自由に、手軽にはできない
- 実行回数、インスタンスの生存時間などの制限がある
- セキュリティ、機密・個人情報保護の観点からリスクがある
といった欠点があります。特に気をつけなければいけないのが、セキュリティ・機密・個人情報の取り扱いです。
色々な観点から解析が行いやすいCode Interpreter 環境ですが、企業秘密を含むような実験データや運用データ、また個人情報を含むようなデータのアップロードはリスクが大きいです。もちろん、実行環境のインスタンスのタイムアウトと一緒に削除されるということになっていますが、本当に削除されるのか? 学習に全く使われないのか? というのは保証が難しいです(バグで流出・残ってしまうリスクは常に存在します)。
こういった欠点については、ローカルの信頼できる環境に Python の実行環境を構築すれば大体解消できます。
しかし、最大の利点は「思いついたことをすぐに実験」できることなので、これはローカルでは実現が難しいです。そこで、両方を併用する形でデータ分析をしていきます。
Python の実行環境を準備
Windows 以外は適当に。
CSV ファイルなどのデータを結合する
システムにもよりますが、Python や Excel で処理するようなデータベースは、汎用的なCSV形式でのエクスポートが用いられることが多いです。ただデータ量が多くなりがちなシステムでは、一度に望むデータ全てをダウンロードできないことも多いです。POS データや EC の販売データは1ヶ月単位や1年単位でしかデータをダウンロードできない、などですね。
Code Interpreter では1度に1ファイルしかアップロードできず、また記事冒頭でも述べた通り生のままのデータをアップロードするのは危険です。
個人情報などのアップロードできないデータを全て削除してからであれば、zip圧縮をして Code Interpreterに処理を任せてしまってもいいと思いますが、ここでは他の下処理のためにもデータを先に結合します。
とはいえ、CSVファイルを1つ1つ表計算ソフトで開いて……なんてやってられないので、 GPT-4 にお願いしてみます。
同じフォルダ内にあるCSVファイルを1つに結合するPythonスクリプトを記述してください。 全てのCSVファイルは同じ列数を持ち、1行目は共通のヘッダー行があります。また、全てのデータはテキストとして扱い、数値などに変換しないようにしてください。
「全てのデータをテキストとして扱う」という注釈を加えているのは、郵便番号や電話番号などの非数値データが数値として扱われてしまうことを避けるためです。今回の用途では電話番号の正確性はさして重要ではないですが、郵便番号は利用するので忘れずに指示しておきます。
Pythonのpandas
ライブラリを使用して、同じフォルダ内の複数のCSVファイルを結合するスクリプトを記述します。以下にそのスクリプトを示します。
import pandas as pd
import glob
# 同じフォルダ内の全てのCSVファイルを取得
csv_files = glob.glob('./*.csv')
# 各CSVファイルを読み込み、データフレームのリストを作成
dfs = [pd.read_csv(f, dtype=str) for f in csv_files]
# 全てのデータフレームを縦に結合
merged_df = pd.concat(dfs, ignore_index=True)
# 結合したデータフレームを新しいCSVファイルとして保存
merged_df.to_csv('merged.csv', index=False)
このスクリプトは、現在のディレクトリ(’./’)の全てのCSVファイルを読み込みます。それぞれのファイルはテキストとして読み込まれ、全てのファイルが1つのデータフレームに結合されます。最後に、結合したデータフレームは新しいCSVファイル(’merged.csv’)として保存されます。
このスクリプトを実行する前に、必要なライブラリがインストールされていることを確認してください。pandas
とglob
が必要で、それらは以下のコマンドでインストールできます。
ただし、このコードを実行するには pandas ライブラリのインストールが必要なので、初めて Python を使う人は、ターミナルなどで
py -m pip install pandas
として pandas ライブラリをインストールしましょう。
また、CSVファイルがWindowsのShift-JISの場合(Excelに合わせる場合は通常この形式)、文字コードにcp932を指定します。
dfs = [pd.read_csv(f, dtype=str, encoding='cp932') for f in csv_files]
完全に初めての場合、Pythonやライブラリのインストールがありますが、それでも10を越える(数万行の)CSVファイルを結合することを考えたら、このスクリプトだけで時間が短縮できます。
また、このスクリプトは汎用的なので、以後の下処理とまとめたスクリプトに(ChatGPTに!)してもらってもいいですし、単体で置いておいてもいいでしょう。
使用するには、コピーしたソースコードを combine.py などの名前で結合したいCSVファイルと同じフォルダにおきます。その上で、 py -m combine.py (Windowsの場合)として実行すれば、combined.csvファイルが出来上がります。
名前やメールアドレスをマスクする
会員カード情報を含む POS データや ECサイトでの売上分析の場合、RFM (Recency Frequency Monetary 最近の購入、購入頻度、購入ボリューム)分析や顧客の LTV(生涯価値)の分析が行えます。
顧客カードなどで一意かつそれ単独では個人を特定できないような ID があればそれを元に個人の行動を元にした統計量を求めることができます。
しかし、ECサイトで会員登録をしていない顧客が多い場合や、予約制の美容室でスタンプカードは発行していても記録されているのは予約の電話番号と名前だけ、といった場合などでは個人情報を元に統計をとる必要があります。他にも、運営しているECサイトが複数あり、横断的に分析したい場合などもID情報は使いづらいでしょう。が、当然そういったデータは個人情報に該当し、Code Interpreterにアップロードするにはリスクが大きすぎます。
よって、ここではメールアドレスと電話番号からユニークなIDを算出してそれをIDとして出力してもらうことにします。もちろん、Code Interpreterにはあげられないので、ChatGPTにPythonスクリプトを書いてもらい、ローカルで実行します。
merged.csvファイルを読み込み、各行の電話番号とメールアドレスをあわせた文字列からユニークなIDを計算してください。 計算したIDはユニークID列として出力csvファイルに含め、電話番号とメールアドレスのデータは出力データから除外してください。 読み込んだデータは全てテキストとして扱い、数値などに変換されないようにしてください。
ここでも郵便番号に配慮するために、読み込みデータをテキストとして扱うように指示します。
ここではPythonのpandasライブラリとhashlibライブラリを使って要求を実行します。ただし、具体的なコードはあなたのデータに依存します。以下に一般的な例を示します。電話番号が “phone_number” 列に、メールアドレスが “email” 列に格納されていると仮定します。
import pandas as pd
import hashlib
# データを読み込む
df = pd.read_csv('merged.csv', dtype=str)
# 電話番号とメールアドレスを結合し、それをハッシュ化してユニークIDを生成
df['unique_id'] = (df['phone_number'] + df['email']).apply(lambda x: hashlib.sha256(x.encode()).hexdigest())
# 電話番号とメールアドレスの列を削除
df = df.drop(['phone_number', 'email'], axis=1)
# 結果を新しいcsvファイルに保存
df.to_csv('output.csv', index=False)
このコードでは、Pythonのhashlibモジュールを使用してSHA256ハッシュを計算しています。SHA256は一般的によく使用される安全なハッシュ関数ですが、必ずしもユニーク性を保証するわけではないことに注意してください。ただし、衝突(異なる入力が同じハッシュ値を生成する現象)は非常にまれなので、実用上はほとんど問題にならないでしょう。
また、上記のコードは入力csvファイルの列名が ‘phone_number’ と ‘email’ であることを前提としています。これらの列名が異なる場合、または複数の電話番号やメールアドレスがある場合など、具体的な状況に応じてコードを適宜調整してください。
最後に、新しく生成した ‘unique_id’ 列を出力csvファイルに含め、電話番号とメールアドレスの列は出力から除外しています。また、index=False
を設定することで、出力csvに不要な行番号が含まれないようにしています。
特に明記していませんが、ユニークIDの算出にはハッシュアルゴリズムを利用して逆算できないようにしています。ここでは、IDを算出した後、メールアドレスと電話番号を削除していますが、算出したIDを保存しつつメールアドレスと電話番号も削除しないCSVファイルをローカルに保存しておけば後でCode Interpreterで行った様々な分析の出力としてIDのリストを得れば、顧客情報を外部に出すことなくマーケティング活動などに生かすことができます。
また、ChatGPTの出力にも含まれていますが、phone_nnumber, emailの両列を実際のデータにあわせる必要があります(compute_hash関数内はただの変数なのでそのままでいいです)。この辺りが実データをアップロードしてその内容を元にスクリプトを生成してくれるCode Interpreterと違って少し煩わしい点ですね。
また、output.csvもhashed.csvなどに変更しておいた方が、後々どのスクリプトの出力か分かりやすくていいかと思います。
加えて、このコードでは欠損値が考慮されていないため、電話番号やメールアドレスに欠損値がある場合にエラーが発生します。対処するには、前処理として
# 欠損値を 'missing' で埋める
df[['pnone_number', 'email']] = df[['phone_number', 'email']].fillna('missing')
として、欠損値に仮の値を入れるようにします。両方空欄だった場合は、当然全て同一の顧客とみられてしまうので、そのようなデータがある場合にはさらに除外するなどの処理が必要です。
知らないライブラリを使わせて郵便番号から都道府県を得る
これまでのように、ChatGPTに単に郵便番号から都道府県を出力するように依頼しても、
hashed.csvファイルを読み込み、各行の郵便番号から都道府県を求めてください。 求めた都道府県は都道府県列として出力csvファイルに含め、郵便番号と住所列は出力データから除外してください。 読み込んだデータは全てテキストとして扱い、数値などに変換されないようにしてください。
この問題を解決するには、日本の郵便番号と都道府県のマッピングが必要です。このマッピングは、日本郵便のウェブサイトからダウンロード可能なデータベースを使用して作成できます。ただし、ここでは具体的な解答を提供するためには、詳細なデータベースと郵便番号の形式について具体的な知識が必要となります。
下記のコードスニペットは、日本の郵便番号と都道府県のマッピングが既に存在し、’postal_code’ 列に郵便番号が格納されていると仮定した場合の一般的な解決策です。
として、Pythonスクリプトも出力しますが、このままでは使い物になりません。GPT-4が賢いとはいえ、学習データのメインは英語圏のものであり、日本語圏での実装例を学習していないことによるものと思います(Bingが使えればまた違うかもしれませんが)。
仕方ないので、Pythonで郵便番号から都道府県を得られるようなライブラリを検索してみます。
すると、このようなAPIキーも必要ないライブラリが見つかったので、これを利用して記述してもらうことにします。このページの説明文をコピペしても動くと思いますが、ここでは Link Reader Pluginを利用してみます。
https://qiita.com/nagataaaas/items/8148ccdcae87ace93327 hashed.csvファイルをpandasライブラリを用いて読み込んでください。読み込んだデータは全てテキストとして扱い、数値などに変換されないようにしてください。また、csvファイルの1行目はヘッダー行で、郵便番号という名前の列が存在しています。 以上のCSVファイルに対し、URLのページ内で紹介されているJushoライブラリを利用して、各行の郵便番号から都道府県を求めてください。 求めた都道府県は都道府県列として出力csvファイルに含め、郵便番号と住所列は出力データから除外してください。
リンクを読み込んでくれ、出力してくれます。が、そのままでは動かなかったのでエラーメッセージをコピー&ペーストして、修正してもらいます。
ドキュメントを読む限り、最初に出力されたコードは間違ってなさそうだったのですが、[0]を付けてlistの先頭を取得しないといけなかったようです。
さらに、欠損値があるとエラーがでるので、修正指示も入れます。
郵便番号が空欄などで検索でエラーが発生したときに都道府県を「不明」とするように変更してください。
import pandas as pd
from jusho import Jusho
# Instantiate the Jusho object
postman = Jusho()
# Read the CSV file
df = pd.read_csv('hashed.csv', dtype=str)
# Create a new column 'prefecture' by applying the Jusho library's function to the 'postal_code' column
def get_prefecture(postal_code):
try:
if postal_code and postman.by_zip_code(postal_code):
return postman.by_zip_code(postal_code)[0].prefecture.kanji
except Exception as e:
print(f"Error occurred with postal code {postal_code}: {e}")
return '不明'
df['prefecture'] = df['postal_code'].apply(get_prefecture)
# Drop the 'postal_code' column
df = df.drop(['postal_code'], axis=1)
# Write the DataFrame to a new CSV file
df.to_csv('prefecture.csv', index=False)
もちろん、Jusho ライブラリをインストールすることを忘れないようにしてください。
今回はECサイトの購入データを想定したため、購入者の位置情報の粒度を都道府県としました。しかし、これが大規模商業施設やイベントであれば市区町村レベルの粒度が適切でしょうし、小売店であれば町域レベルの粒度が求められるかと思います。
とはいえ、マーケティングの分析で使われるジオグラフィック情報としては郵便番号だけあれば恐らくほぼ充分だと思います。
その他の情報
例えばデータベースに生年月日まで含まれている場合だと、年齢だけに直したり、あるいは20代、30代……のように幅を持たせたかたちに変換するといいでしょう。
これは個人情報保護の観点からもそうですが、余りに粒度が細かいとCode Interpreter上とはいえ、指示が面倒くさいことになるからです。
CodeInterpreterにアップロードする
表計算ソフトなどで、その他の個人の特定につながる情報がないことを確認したら、Code Interpreterにアップロードして簡単な分析をしてみます。
アップロードしてRFM分析しろ、だけではさすがに動作しないようです。よって、以下のように追加で指示を出します。
顧客識別情報:unique_id 列に含まれます。
最終購入日:成約日 列に含まれる中で最も新しい日付が最終購入日です
購入頻度:成約日 から算出してください
購入金額:金額 列を合計してください。受注番号 列が同一の場合は複数回加算しないでください。
RFM分析の結果、以下のようなテーブルを作成しました。ここには、各顧客の「最終購入日からの経過日数(Recency)」、「購入回数(Frequency)」、「合計購入金額(Monetary_Value)」が表示されています。
[表略]このテーブルには、データの最初の数行の情報しか表示されていません。全ての顧客情報が含まれています。次に何を行えばよいでしょうか?
集計は出来たようなので、作図してもらいます。
正直、なんかいい感じに分割してくれないかなぁと思ったのですが、一発では難しいようです。
それでも、適当な指示をしたらいい具合に分割してくれるようです。
一応、動いてはいますが正直何か意味が見いだせるか? というと微妙なグラフです(まあ、指示が雑だからですが)。この辺りは、R, F, Mそれぞれについてまずは分布を確認してから分割するのが正しいように思います。
ついでに、ジオグラフィック情報も利用してみます。
人口が多い東京都に偏っていますね。また、CSVファイルが壊れていることが見て取れます。恐らく、CSVファイルに含まれるコメントなどが悪さをしたのだと思われます。
対処としては、最初から異常の原因になりそうな列はエクスポートせず、読み込まないことや出力をExcelなどのファイルにする(カンマやダブルクォーテーションの扱いに神経質にならなくてよい)などの対処方法が考えられます。
また、今回は処理ごとにステップを踏んでスクリプトを作成しましたが、一括で処理してしまうことでもエラーを防ぎやすくなると思います。
終わりに
Code Interpreterを使う場合、今回の記事でRFM分析をしたように、使用者側が割とふんわりとした「なんとなくRFM分析しなきゃ!」という発想からスタートしても、ChatGPT Code Interpreter 側が質問をすることで、きちんと動作させることができる点が非常に優れていると言えます。
追加で入力が必要という意味では、最初から「Recency はこうやって求める、Frequencyはこう、受注番号が同一の場合は、同じ注文として扱う」のような指示をしっかり出しても変わりないですし、その方が早いです。ただし、それには指示を出す側が慣れている必要があります。
一方で、Code Interpreter を使う場合には、慣れていない状態で「相談しながら」、「出力を確認しながら」操作することが非常にスムーズに進む点が優れていると言えます。
次は、エラーのないデータを用いて Code Interpreterを使った分析を試してみたいと思います。