派遣で働くエンジニアのスキルアップを応援するサイト

PRODUCED BY RECRUIT

【第8話】ロジスティック回帰で、翌月の購買実績を予測してみよう!実践編【漫画】未経験なのに、機械学習の仕事始めました

f:id:itstaffing:20211020101914j:plain
f:id:itstaffing:20211020101917j:plain
f:id:itstaffing:20211020101919j:plain

★この記事で利用するサンプルコードは、こちらで確認できます。

モデル構築ライブラリ「scikit-learn」とは?

ロジスティック回帰の概要を第7話で紹介しました。ロジスティック回帰とは、簡単にいうと、データが与えられたときにAかBのどちらに属するのかを予測する方法です。

今回は、機械学習において最も有名なライブラリの一つである、scikit-learn(サイキット・ラーン)を用いて、ロジスティック回帰でモデルを構築します。

このライブラリはPythonから利用可能で、さまざまな機械学習手法を一貫したインタフェースで利用できます。つまり、一度ロジスティック回帰で予測モデルを構築する方法を学んだら、他の予測モデルも同じようにして作ることができるというメリットがあるのです。

▼ロジスティック回帰(LogisticRegression)以外にも、モデルを作るための他の機械学習手法が大量に用意されている

f:id:itstaffing:20211020101922j:plain

https://scikit-learn.org/stable/modules/classes.html#module-sklearn.linear_model

>> 拡大表示

f:id:itstaffing:20210113102529j:plain
ロジスティック回帰以外の手法でも、たくさん用意されている……だと…?自由度が高すぎて、使い方がむずかしそうなんですけど。
f:id:itstaffing:20210113102532j:plain
大丈夫!scikit-learnの使い方を紹介する技術書やブログって、とっても多いんです。つまずいてしまった場合も、役立つ記事や書籍に出会いやすいですよ。
f:id:itstaffing:20210113102524j:plain
よく使われているライブラリだから、本やブログで情報を集めやすいんだ!手順やサンプルコードが豊富に用意されているのは、初心者には助かるなぁ。
f:id:itstaffing:20210113102532j:plain
公式ドキュメントが充実していることも魅力の一つですね。

たとえばこちらのページでは、ロジスティック回帰を数式レベルから解説しています。このドキュメントの数式通りにライブラリが実装されているので、数式とコードの両面から手法を学べます。

f:id:itstaffing:20211020101924j:plain

https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression

>> 拡大表示

f:id:itstaffing:20210113102532j:plain
私個人の感想ですが、scikit-learnの実装は綺麗に書かれていると思うんですよね。 なので、Python自体の学習にも役立つと思います!
f:id:itstaffing:20210113102524j:plain
オープンソースで公開されてるんだ!個人、商用に関わらず、誰でも無料で使えるのはありがたいですね。

モデル構築ライブラリ「scikit-learn」を使ってみよう

f:id:itstaffing:20210113102532j:plain
それではさっそく、scikit-learn を使ってみましょう。次のようにコマンドを実行してくださいね。

・scikit-learn をインストールする

pip install scikit-learn

※Pythonの環境構築の手順は割愛します。

・ロジスティック回帰を利用する場合、次のようにしてクラスを読み込む

from sklearn.linear_model import LogisticRegression

・予測モデル構築の準備をする

model = LogisticRegression()

f:id:itstaffing:20210113102532j:plain
ロジスティック回帰をするには、sklearn.linear_model の中に LogisticRegressionモデルというのが用意されているので、上のコマンドのようにインポートして使うといいですよ。公式ドキュメントはこちらです。

さて、モデルを構築するための学習データは model.fix(X, y)で適用できます。

Xは特徴量と呼ばれるデータです。今回の例では、第6話で紹介したRFM分析の際に計算したデータを使います。

Recency(直近いつ)、Frequency(頻度)、Monetary(購入金額)を列に持ち、行方向にそれぞれの顧客が並んでいるデータです。

1番目の顧客のRFMは、 286, 0.002994, 77183.60
2番目の顧客のRFMは、 0, 0.017964, 4085.18

といったように、データが顧客数の数だけ並んでいるといった具合です。

yは翌月の購入実績の有無です。購入していた場合は True、購入していない場合はFalseとなる配列です。このデータについても、第6話で紹介したとおりです。

ロジスティック回帰で予測するには、次のように実行します。

・ロジスティック回帰で予測する

from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X, y)
model.predict(X)

最後のmodel.predict(X)を実行すると、予測結果を得られます。

それぞれの顧客に対して、翌月に購入すると予測した場合はTrue、購入しないと予測した場合はFalse が返ってきます。モデルの予測精度を確認する方法の一つとして分割表があります。実績値と予測値の集計で、それぞれの項目に名前がついています。

f:id:itstaffing:20211020101931j:plain

新型コロナウイルスの PCR 検査に関連して、偽陽性(False Positive、略して FP)という言葉を聞いたことがあるかもしれません。感染していないにもかかわらず、検査結果が陽性と判定されてしまうケースです。機械学習においても偽陽性が発生します。翌月に購入しないにもかかわらず、購入すると予測してしまったケースです。予測通りに顧客が商品を購入した場合真陽性(True Positive、略して TP)、逆に予想通りに購入しなかった場合は真陰性(True Negative、略して TN)と言います。

Pandasで分割表を計算する場合はcrosstabを利用します。

  pd.crosstab(
    y,
    model.predict(X),
    rownames=["購入実績"],
    colnames=["予測結果"],
  )

f:id:itstaffing:20211020101933j:plain

全件に対して予測が的中した割合を正解率(Accuracy)と呼びます。これは、予測モデルの精度を比較する指標のひとつとして有名です。正解率は、(TN+TP)/(TN+FP+FN+TP) で計算します。今回のケースでは、正解率は(2515+287)/(2515+118+1054+287)≒0.705 となります。

f:id:itstaffing:20211020101927j:plain

次回は、予測モデルの精度を計算する方法であるクロスバリデーションを紹介します。お楽しみに!

第1話 機械学習の仕事内容って?実はコードを書くだけじゃない!
第2話 人工知能、機械学習、ディープラーニングの違いとは?
第3話 機械学習の活用事例!建設機械や回転寿司屋でも活用されている!?
第4話 機械学習したいのにデータがない!?
第5話 集計と可視化:pandasでデータの加工をしてみよう
第6話 続!集計と可視化:Plotlyでデータをグラフ化して傾向をとらえよう
第7話 ロジスティック回帰ってなあに?紹介編

【筆者】
早川 敦士さん
株式会社FORCASの分析チームにてリーダーを務める傍らで、株式会社ホクソエムで執行役員として従事。新卒でリクルートコミュニケーションズに入社しWeb広告やマーケティングオートメーションなどのB2Cマーケティングを経験し、FORCASではB2Bマーケティングプラットフォームのデータ分析および開発を担当している。大学在学中に『データサイエンティスト養成読本』(技術評論社刊)を共著にて執筆。その後も『機械学習のための特徴量エンジニアリング』(オライリー・ジャパン刊)や『Pythonによるはじめての機械学習プログラミング』(技術評論社刊)などで執筆活動を続けている。国内最大級のR言語コミュニティであるJapan.Rを主催。Youtubeチャンネル『データサイエンティストgepuro』で動画を投稿。
・Twitterアカウント

湊川 あいさん
フリーランスのWebデザイナー・漫画家・イラストレーター。マンガと図解で、技術をわかりやすく伝えることが好き。 著書『わかばちゃんと学ぶ Git使い方入門』『わかばちゃんと学ぶ Googleアナリティクス』『わかばちゃんと学ぶ Webサイト制作の基本』『運用ちゃんと学ぶ システム運用の基本』『わかばちゃんと学ぶ サーバー監視』が発売中のほか、マンガでわかるGit・マンガでわかるDocker・マンガでわかるRuby・マンガでわかるScrapbox・マンガでわかるLINE Clova開発・マンガでわかる衛星データ活用といった分野横断的なコンテンツを展開している。
・Amazon著者ページ
・Twitterアカウント

わかばちゃんが登場する書籍いろいろ

イメージ イメージ
イメージ イメージ

わかばちゃんと学ぶシリーズ(Amazonページ)

 

第6話 ExcelからAccessの読み書き【連載】実務で使えるAccessのコツ

f:id:itstaffing:20210924112110j:plain

データベース管理ソフトウェア、Accessを実務で使うときに「知っているとちょっと便利なコツ」について紹介するこのコラム。今回は、Excelと連携させることの最大のメリットをご紹介します。あわせて知っておきたい、悪意のある第三者にどう対応するかのセキュリティ面についても解説。最終回となる今回のお話しも、あなたのAccess力アップに役立ちますように。

前回のコラムを見逃した方はこちらからご覧ください。
第5話 VBA内でSQLを扱うコツ

【筆者】今村 ゆうこさん
【筆者】今村 ゆうこさん
地方の非IT系企業に勤務し、業務アプリケーション開発を行うプログラマー。著書のイラストや図解も手掛けている。大学卒業後出身地へUターン就職し、2度の産休/育休を経て現在は小学生と保育園児の2人のこどもを抱えるワーキングマザー。主にVBAを使ってデータベースの情報を加工/展開するアプリを開発するほか、WordPressを使った個人ブログ(https://ateitexe.com/)で技術情報を発信している。著書に『Accessデータベース 本格作成入門』『Excel & Access連携 実践ガイド』(技術評論社)など。

ExcelとAccessを連携させると…?

Excelの次のステップアップとしてAccessにチャレンジしてみよう、という気持ちで始める方も多いことと思います。データベースの構造を知ることで効率の良いデータの管理方法がわかり、結果的にExcelを活用する力も向上するはずです。

しかし、AccessはExcelの上位互換というわけではありません。Excelはグラフ化や視覚的な分析が得意ですが、入力の自由度が高すぎるためにデータを整然と保つのが苦手です。対してAccessはデータ管理のスペシャリストですが、Excelほどに自由度の高いUIやグラフ化は苦手なのです。

f:id:itstaffing:20210924112202j:plain

最終回である今回は、そんなお互いの苦手な部分を補完し合うと便利な例をご紹介いたします。

Excelにデータを持たなくていい

連携させることの最大のメリットは、Excelでデータの管理をしなくてもよくなることです。管理はAccessにまかせて、Excel側では必要なときに必要なデータだけ取り出して表やグラフに活用すれば、お互いのソフトの「いいとこ取り」でスマートに使うことができます。

Accessが苦手なユーザーにも受け入れられやすい

Microsoft Officeシリーズの仲間とはいえ、一般ユーザーからするとAccessの認知度は高くありません。慣れ親しんだExcelのシートだと心理的な抵抗感が少ないユーザーも多くいらっしゃるので、UI部分をExcelで作るのも、ユーザーの「使いやすい」と感じる1つの要素になり得ます。

読み込み即グラフ化が可能

たとえば以下のようなExcelシートがあるとします。上部のグラフは下部の表に紐付いており、表に数値が入ると棒グラフが描写される設定になっています。

f:id:itstaffing:20210924112217j:plain

このシートのB2セル(年)を変更してマクロを実行すれば、その年に応じたデータが読み込まれてグラフが描写される、なんて素敵だと思いませんか?そんなことが可能なんです。

読み書きのテンプレート

それでは、Accessのテーブルから読み書きするためのテンプレートを任意のExcelファイル(.xlsm)のVBEに書いていきます。

今回は参照設定の「Microsoft ActiveX Data Objects X.X Library」にチェックを入れずにADOを利用するコードでご紹介します。詳しくは第4回の記事も合わせてご覧ください。

以下はExcelからAccessのファイルを指定して接続したり、接続解除したりする部分を切り出してプロシージャ化したものです。何度も使われる部分になるので、こうして部品化しておくと取り回しが楽になります。

上の3行がモジュールの先頭(宣言セクション)に書かれるように、新しい標準モジュールにコピー&ペーストし、Accessのパスは任意のものへ変更してください。

Private cn As Object  'ADOコネクション用オブジェクトの宣言
Private Const adStateOpen = 1  'レコードセットが開いている場合の設定値
Private Const adOpenKeyset = 1  'レコードセットカーソルタイプの設定値

Private Sub connectDB()
  '## 接続

  Set cn = CreateObject("ADODB.Connection") 'ADOコネクションを作成
  cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
      "Data Source=C:\data.accdb;"  'Accessファイルを指定してコネクションを開く
End Sub

Private Sub disconnectDB()
  '## 接続解除

  If Not cn Is Nothing Then
    cn.Close
    Set cn = Nothing
  End If
End Sub

以下はレコードの抽出で使用するテンプレートです。引数のSQLによってAccessから抽出されたレコードを、Excelの指定シート・セル起点で展開します。上の接続/接続解除のプロシージャと同じモジュールに書いてください。

Public Sub putRsOnSheet(ByVal sql As String, ByVal ws As Worksheet, ByVal rng As Range)
  '## レコードセットをシートへ展開

  On Error GoTo Err_Handler  'エラーが起きたら「ErrorHandler」にジャンプする指示

  Call connectDB  '接続

  'レコードセットのオープン
  Dim rs As Object  'レコードセット用変数宣言
  Set rs = CreateObject("ADODB.RecordSet")  'ADOレコードセットオブジェクトを作成
  rs.Open sql, cn  'レコードセットを開く

  '空だったら最終処理へ
  If rs Is Nothing Or (rs.BOF And rs.EOF) Then  'レコードセットやレコードが存在しなかった場合
    MsgBox "対象レコードがありません", vbInformation, "確認"  'メッセージ出力
    GoTo Finally  '最終処理へジャンプ
  End If

  'シートに展開
  Dim tgtRow As Long
  tgtRow = rng.Row  'スタートの行
  Dim tgtCol As Long
  tgtCol = rng.Column  'スタートの列
  Application.ScreenUpdating = False  '画面更新OFF
  Do Until rs.EOF  'レコードセットが終了するまで処理を繰り返す
    Dim fldNum As Long  'フィールドの繰り返し用変数
    For fldNum = 0 To rs.Fields.Count - 1  'フィールドの数だけ繰り返す
      ws.Cells(tgtRow, tgtCol + fldNum).Value = rs(fldNum)  'フィールドの並び順でセルに書き込む
    Next fldNum  '次のフィールドへ
    tgtRow = tgtRow + 1  '行をカウントアップする
    rs.MoveNext  '次のレコードに移動する
  Loop
  Application.ScreenUpdating = True  '画面更新ON

  GoTo Finally  '正常に終了したら最終処理へジャンプ

Err_Handler:  '例外処理
  Dim msgTxt As String
  msgTxt = "Error #: " & Err.Number & vbNewLine & vbNewLine & Err.Description  'エラーメッセージが入る
  MsgBox msgTxt, vbOKOnly + vbCritical, "エラー"  'メッセージ出力

Finally:  '最終処理
  If Not rs Is Nothing Then  'レコードセットオブジェクトが存在している場合のみ
    If rs.State = adStateOpen Then rs.Close  'レコードセットが開いていたら閉じる
    Set rs = Nothing
  End If

  Call disconnectDB  '接続解除
End Sub

以下はレコードの挿入・更新・削除で使用するテンプレートです。引数のコレクション内に格納されているすべてのSQLを実行します。上の接続/接続解除のプロシージャと同じモジュールに書いてください。

Public Function tryExecute(ByVal sqlList As Collection) As Boolean
  '## SQLの実行

  On Error GoTo ErrorHandler  'エラーが起きたら「ErrorHandler」にジャンプする指示

  Call connectDB  '接続

  cn.BeginTrans  'トランザクション開始

  '実行
  Dim sql As Variant
  For Each sql In sqlList  'SQL文リストをループ
    cn.Execute sql  '1行ずつ実行
  Next sql

  cn.CommitTrans  '確定

  tryExecute = True  '成功だった場合、関数の結果にTrueを入れる
  GoTo Finally  '正常に終了したら最終処理へジャンプ

ErrorHandler:  '例外処理
  cn.RollbackTrans  '元の状態へ戻す
  Dim msgTxt As String
  msgTxt = "Error #: " & Err.Number & vbNewLine & vbNewLine & Err.Description  'エラーメッセージが入る
  MsgBox msgTxt, vbOKOnly + vbCritical, "エラー"  'メッセージ出力

Finally:  '最終処理
  Call disconnectDB  '接続解除
End Function

テンプレートの利用(レコードをExcelシートに展開)

SQLのSELECT構文を作成し、起点とするシート・セルと共に引数としてテンプレートへ渡すコードがこちらです。

Public Sub loadRecord()
  '## レコードをシートへ

  '対象シートを設定
  Dim ws As Worksheet
  Set ws = Sheets("シート名")

  'SQL文の作成
  Dim sql As String
  sql = "任意のSELECT文"

  'レコードセットをシートへ展開
  Call putRsOnSheet(sql, ws, ws.Range("A1"))
End Sub

実行すると、指定のセルを起点にレコードセットが展開されます。

さきほど例に出した、グラフが設定されているシートに展開するコードだったら、以下のように書きます。SQLではPIVOTを使ったクロス集計を行っています。

(接続するAccessファイルに「販売」テーブルが存在し、その中に「商品ID(文字列型)」「売上日(日付型)」「数量(数値型)」フィールドがある想定です)

Public Sub loadRecord()
  '## レコードをシートへ

  '対象シートを設定
  Dim ws As Worksheet
  Set ws = Sheets("集計")

  '指定年取得
  Dim tgtYear As Long
  tgtYear = ws.Range("B2").Value

  'SQL文の作成
  Dim sql As String
  sql = _
    "TRANSFORM Sum(数量) " & _
    "SELECT 商品ID " & _
    "FROM 販売 " & _
    "WHERE Format(売上日,'yyyy') In ('" & tgtYear & "')" & _
    "GROUP BY 商品ID " & _
    "PIVOT Format(売上日,'m') In ('1','2','3','4','5','6','7','8','9','10','11','12');"

  'レコードセットをシートへ展開
  Call putRsOnSheet(sql, ws, ws.Range("B16"))
End Sub

実行すると以下のようにデータが展開され、自動的にグラフが描写されます。

f:id:itstaffing:20210924112223j:plain

B2セルの値を変更してプログラムを実行し直すことで、その年のデータを月別に読み込むことができます。SQLを工夫すれば、年月を指定して日別のグラフにすることも可能です。

テンプレートの利用(レコードの挿入・更新・削除)

SQLのINSERT/UPDATE/DELETE構文をコレクションに格納し、引数としてテンプレートへ渡して結果をTrue/Falseの形で受け取るコードがこちらです。

Public Sub executeSQL()
  '## レコードの挿入・更新・削除

  'コレクションの作成
  Dim sqlList As Collection
  Set sqlList = New Collection

  'SQL作成
  Dim sql As String 'SQL文用変数の宣言

  sql = "任意のINSERT/UPDATE/DELETE構文"  '1つ目のSQL文
  sqlList.Add sql  'コレクションへ追加

  sql = "任意のINSERT/UPDATE/DELETE構文"  '2つ目のSQL文
  sqlList.Add sql  'コレクションへ追加

  '実行
  If tryExecute(sqlList) Then  '処理が成功した場合
    MsgBox "正常に終了しました", vbOKOnly + vbInformation, "終了"  '終了メッセージ
  End If
End Sub

実行すると、問題が起きた場合は処理されずにエラー内容が、問題なく処理が終了した場合は終了メッセージが表示されます。

f:id:itstaffing:20210924112230j:plain

SQLにExcel上のセルの値を組み込めば、自由度の高いシステムを作ることができます。

VBAでプレースホルダーを使う

ユーザーが入力した値をそのままSQLに組み込んでデータベースを操作する場合、さらにその入力する人間にSQL知識と悪意がある場合は、SQLインジェクション(不正なSQL文を実行させる攻撃)の恐れがあることに注意をしなければなりません。

Accessは個人~少人数向けの非公開が前提のデータベースソフトなので悪意のある第三者が介入する可能性は低いと思われますが、知識として知っておくことは大切です。

この問題に対しては、入力値を一旦別の場所に確保(プレースホルダー)しておいて、実行のときに無害化して使うのが一般的な手段で、VBAでも実装が可能です。

それでは最後に、VBAでプレースホルダーを使った書き方も見ておきましょう。

以下はレコードの抽出で使用するテンプレートです。レコードセットを取得してイミディエイトウィンドウへ出力する動きを、1つのプロシージャで完結する形で書いてあります。SQL条件の値に「?」を置き、プレースホルダーの値を配列型でパラメーターとして持たせます。

Public Sub loadRecord ()
  '接続
  Dim cn As Object  'ADOコネクション用オブジェクト
  Set cn = CreateObject("ADODB.Connection")
  cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
        "Data Source=C:\Data.accdb;"  'Accessファイルを指定してコネクションを開く

  'ADOコマンドオブジェクトの設定
  Dim cmd As Object
  Set cmd = CreateObject("ADODB.Command")
  cmd.ActiveConnection = cn

  'ADOレコードセットオブジェクトの設定
  Dim rs As Object
  Set rs = CreateObject("ADODB.RecordSet")

  '実行
  cmd.CommandText = "SELECT * FROM table1 WHERE f1 = ? ;"
  Set rs = cmd.Execute(Parameters:=Array("a"))

  Do Until rs.EOF  'レコードセットが終了するまで処理を繰り返す
    Dim fldNum As Long  'フィールドの繰り返し用変数
    For fldNum = 0 To rs.Fields.count - 1  'フィールドの数だけ繰り返す
        Debug.Print rs(fldNum), ;  'フィールドを改行せずイミディエイトウィンドウに出力
    Next fldNum  '次のフィールドへ
    Debug.Print ""  'レコード切り替えでイミディエイトウィンドウを改行
    rs.MoveNext  '次のレコードに移動する
  Loop

  'レコードセットオブジェクトの処理
  rs.Close  'レコードセットを閉じる
  Set rs = Nothing

  'コマンドオブジェクトの後処理
  Set cmd = Nothing

  '接続解除
  cn.Close
  Set cn = Nothing
End Sub

以下はレコードの挿入・更新・削除で使用するテンプレートです。1つのプロシージャで完結する形で書いてあります。「?」を複数使用する場合は、パラメーターの配列に順番に配置します。

Public Sub executeSQL()
  '接続
  Dim cn As Object  'ADOコネクション用オブジェクト
  Set cn = CreateObject("ADODB.Connection")
  cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
        "Data Source=C:\Data.accdb;"  'Accessファイルを指定してコネクションを開く

  'ADOコマンドオブジェクトの設定
  Dim cmd As Object
  Set cmd = CreateObject("ADODB.Command")
  cmd.ActiveConnection = cn

  '実行
  cmd.CommandText = "INSERT INTO table1(f1, f2, f3, f4) VALUES(?, ?, ?, ?);"
  cmd.Execute Parameters:=Array(1, Now, "a", False)

  'コマンドオブジェクトの後処理
  Set cmd = Nothing

  '接続解除
  cn.Close
  Set cn = Nothing
End Sub

Excelからデータベースを操作できると、仕事の可能性がぐんと広がります。対象のデータベースの規模が大きくなれば気をつけなくてはならないことも増えるので、セキュリティ面も勉強しながらステップアップを目指してくださいね。

以上で、連載「実務で使えるAccessのコツ」は終了です。この連載が、あなたのお仕事に役に立てたらとっても嬉しいです。ここまで読んでいただき、ありがとうございました!

第1話「イベント」を使いこなそう
第2話 レイアウトで躓きやすいポイント
第3話 マクロとVBAの違い
第4話 DAOとADOの違い
第5話 VBA内でSQLを扱うコツ

 

第5話 VBA内でSQLを扱うコツ【連載】実務で使えるAccessのコツ

f:id:itstaffing:20210827161938j:plain

データベース管理ソフトウェア、Accessを実務で使うときに「知っているとちょっと便利なコツ」について紹介するこのコラム。今回は、VBA内に複雑なSQLを組み込むための手順を解説します。複雑なものほど長くなるSQLですが、なにを使って区切ると読みやすくなるのでしょうか。あなたのAccess力アップに役立ちますように。

前回のコラムを見逃した方はこちらからご覧ください。
第4話 DAOとADOの違い

【筆者】今村 ゆうこさん
【筆者】今村 ゆうこさん
地方の非IT系企業に勤務し、業務アプリケーション開発を行うプログラマー。著書のイラストや図解も手掛けている。大学卒業後出身地へUターン就職し、2度の産休/育休を経て現在は小学生と保育園児の2人のこどもを抱えるワーキングマザー。主にVBAを使ってデータベースの情報を加工/展開するアプリを開発するほか、WordPressを使った個人ブログ(https://ateitexe.com/)で技術情報を発信している。著書に『Accessデータベース 本格作成入門』『Excel & Access連携 実践ガイド』(技術評論社)など。

業務で使えるSQLを作るには

前回の記事では、AccessVBAでSQLを「処理する部分」のテンプレートの書き方をご紹介しました。このテンプレートがあれば、SQLだけ書き換えれば自由度の高い使い方ができてとっても便利です。

ただ、この「SQLを書き換えるだけ!」が意外と難しくて、固定値の短いSQL文ならばそれほどではありませんが、業務で使うとなるとどうしても複雑になりがちです。

そういったSQL文は細かいパズルのようで、うっかり必要な記述が抜けてしまったり、そのミスに気づきにくかったり、慣れるまではゴールが遠く感じてしまうかもしれません。

f:id:itstaffing:20210827161940j:plain

今回は、そんなSQLパズルの組み立て方のコツをご紹介します。

SQLの基本形をおさらい

まずはSQLでレコード操作するための基本の文を見てみましょう。

・抽出 → SELECT フィールド1, フィールド2 FROM テーブル WHERE 条件;
・挿入 → INSERT INTO テーブル(フィールド1, フィールド2) VALUES(値1, 値2);
・更新 → UPDATE テーブル SET フィールド1=値1, フィールド2=値2 WHERE 条件;
・削除 → DELETE FROM テーブル WHERE 条件;

使うときに大切なのは、フィールドのデータ型です。値はデータ型に適した形で書かなければなりません。

上のINSERT文を例にすると、たとえばフィールド1が数値型でフィールド2がブーリアン型の場合、対応する値は以下のように書きます。*真(true)か偽(false)のどちらかの値が入る

f:id:itstaffing:20210827161943j:plain

フィールド1が文字列型だった場合は「'」を、フィールド2が日付型だった場合「#」を、対応する値にそれぞれ付けることでデータ型に対応させます。

f:id:itstaffing:20210827161947j:plain

この識別記号の扱いをよく覚えておきましょう。

VBA内ではSQLは“文字列”で扱う

VBA内ではSQLはそのまま認識できないので、文字列として扱うことになります。前回の記事で紹介したテンプレートを使う側では、レコードを抽出するコード(抜粋)は以下で、

Public Sub selectRecord()
  '## レコードの抽出

  'SQL作成
  Dim sql As String
  sql = "任意のSELECT構文"

  '以下略
End Sub

レコードを挿入・更新・削除するコード(抜粋)は以下のようになっていましたよね。

Public Sub executeSQL()
  '## レコードの挿入・更新・削除

  'コレクションの作成
  Dim sqlList As Collection
  Set sqlList = New Collection

  'SQL作成
  Dim sql As String 'SQL文用変数の宣言

  sql = "任意のINSERT/UPDATE/DELETE構文"
  sqlList.Add sql 'コレクションへ追加

  '以下略
End Sub

このように、VBA内でのSQLは、「"」で括ることで、文字列として扱います。SQLの文字列は「'」、VBAの文字列は「"」と、使い分けておきましょう。

さきほど例に挙げた、データ型の識別記号を含めたINSERT文をVBA内に書くならば、以下のようになるわけです。

f:id:itstaffing:20210827161916j:plain

ここまで、しっかり理解しておきましょう。

VBA内に複雑なSQLを組み込むための手順

それではもう少し具体的に、VBA内で変数を組み込んだSQL文を作成してみます。

以下は「販売」テーブル内の「ID」フィールド(文字列型)、「売上日」フィールド(日付型)へ値を挿入するSQLです。

f:id:itstaffing:20210827161920j:plain

1.まずは1行の文字列にする

f:id:itstaffing:20210827161923j:plain

はじめに、「"」で括っただけの1つの文字列でレコード操作ができるか確認してください。このとき、適切な場所に「半角スペース」が含まれているかが大切です。必要なスペースが入っていないと、この時点でエラーになってしまいます。

2.句の区切りで分割する

f:id:itstaffing:20210827161929j:plain

複雑なSQLは長くなるので、VALUES、WHEREなどの区切りで分割すると読みやすくなります。「"」で括られている文字列が2つになり、文字列同士を「&」で結合しています。

<p">このとき、「VALUES」直前のスペースを忘れないように気をつけてください。スペースをどちらの文字列に含むかは好みですが、統一しておくとよいでしょう(筆者はスペースを文字列の最後に含めるようにしています)。

3.改行する

f:id:itstaffing:20210827161926j:plain

VBE(Visual Basic Editor)内で、改行は「 _」(半角スペース+アンダースコア)です。2つに分割した文字列を改行して、インデントを入れて行頭を揃えましょう。

この改行はVBA上のもので、文字列合成されたSQL文内には改行は含まれません。

4.フィールドごとに分割&改行する

f:id:itstaffing:20210827161932j:plain

フィールドがたくさんある場合は、フィールドと値を1つの要素ずつ改行しておくと比較的読みやすくなります。このとき、インデントを入れておくとさらに良いでしょう。最終的にSQLが正しく認識されれば動作するので、あまり細かく分割されすぎないほうがよい方は適宜で構いません。

5.可変部分を分割する

f:id:itstaffing:20210827161935j:plain

「P001」や「2021/1/1」は、実務では変数を使いたい部分です。「'」や「#」などの識別子を分割して、可変部分のみ切り出します。

6.変数に置き換える

f:id:itstaffing:20210827161935j:plain

可変部分を変数に置き換えます。変数部分は、Form.Textbox.Valueのように書けば、フォーム上のテキストボックスの値を組み込むこともできます。

以上の手順はあくまで一例ですので、ご自身の理解しやすい形にしていただければと思います。

なお、Accessはインハウスデータベース(企業などが自社で利用するための情報を非公開で蓄積するデータベース)の中でも非常に少人数かつ限定的に使われるものなので、SQLインジェクション(不正なSQL文を実行させる攻撃)の可能性は低いと思われますが、VBAでも対策は可能です。こちらは次回記事でご紹介いたします。

例)テーブル結合があるSELECT文

それでは次は、よく登場する複数テーブルからのSELECT文を見てみましょう。一文で書くと以下のように長くて、読み解くのがつらいですね。しかし、この時点でちゃんと動くかどうか確認しておきましょう。ここに問題があった場合、後で原因をたどるのが困難になってしまいます。

sql = "SELECT T1.販売ID, T1.商品ID, T2.商品名, T1.売上日, T1.数量, T2.定価 FROM 販売データ AS T1 INNER JOIN 商品マスター AS T2 ON T1.商品ID = T2.商品ID WHERE T1.売上日 BETWEEN #2021/01/01# AND #2021/01/30# ORDER BY T1.売上日 DESC;"

まずは大きな区切り(上記で色が付いている部分)で文字列を分割・改行します。インデントで行頭を揃えるのも忘れずに。これだけでもかなり読みやすくなりましたね。

sql = _
  "SELECT T1.販売ID, T1.商品ID, T2.商品名, T1.売上日, T1.数量, T2.定価 " & _
  "FROM 販売データ AS T1 INNER JOIN 商品マスター AS T2 ON T1.商品ID = T2.商品ID " & _
  "WHERE T1.売上日 BETWEEN #2021/01/01# AND #2021/01/30# " & _
  "ORDER BY T1.売上日 DESC;"

FROM句がまだ情報量が多いので改行してみましょう。関連項目はさらに一段インデントを入れておくとわかりやすくなります。

sql = _
  "SELECT T1.販売ID, T1.商品ID, T2.商品名, T1.売上日, T1.数量, T2.定価 " & _
  "FROM 販売データ AS T1 " & _
    "INNER JOIN 商品マスター AS T2 " & _
    "ON T1.商品ID = T2.商品ID " & _
  "WHERE T1.売上日 BETWEEN #2021/01/01# AND #2021/01/30# " & _
  "ORDER BY T1.売上日 DESC;"

現時点ではWHERE句の条件は1つだけですが、複数設定する場合は、条件ごとに改行してインデントを入れると可読性が良くなります。

sql = _
  "SELECT T1.販売ID, T1.商品ID, T2.商品名, T1.売上日, T1.数量, T2.定価 " & _
  "FROM 販売データ AS T1 " & _
    "INNER JOIN 商品マスター AS T2 " & _
    "ON T1.商品ID = T2.商品ID " & _
  "WHERE " & _
    "T1.売上日 BETWEEN #2021/01/01# AND #2021/01/30# " & _
    "AND T1.商品ID = 'P001' " & _
  "ORDER BY T1.売上日 DESC;"

フィールドの改行はお好みですが、改行する場合はインデントを入れると良いでしょう。

sql = _
  "SELECT " & _
    "T1.販売ID, " & _
    "T1.商品ID, " & _
    "T2.商品名, " & _
    "T1.売上日, " & _
    "T1.数量, " & _
    "T2.定価 " & _
  "FROM 販売データ AS T1 " & _
    "INNER JOIN 商品マスター AS T2 " & _
    "ON T1.商品ID = T2.商品ID " & _
  "WHERE " & _
    "T1.売上日 BETWEEN #2021/01/01# AND #2021/01/30# " & _
    "AND T1.商品ID = 'P001' " & _
  "ORDER BY T1.売上日 DESC;"

WHERE句の条件値を変数に置き換えます。こうしておけば、柔軟に条件を変えて抽出することができます。「'」や「#」の識別子の有無に注意しながら記述しましょう。

sql = _
  "SELECT " & _
    "T1.販売ID, " & _
    "T1.商品ID, " & _
    "T2.商品名, " & _
    "T1.売上日, " & _
    "T1.数量, " & _
    "T2.定価 " & _
  "FROM 販売データ AS T1 " & _
    "INNER JOIN 商品マスター AS T2 " & _
    "ON T1.商品ID = T2.商品ID " & _
  "WHERE " & _
    "T1.売上日 BETWEEN #" & date1 & "# AND #" & date2 & "# " & _
    "AND T1.商品ID = '" & id & "' " & _
  "ORDER BY T1.売上日 DESC;"

なお、文字列の改行が多くなりすぎると認識されなくなってしまうので、長くなる場合はいったん終わらせて、改めて今まで書いた文字列と結合するという方法もあります。

sql = _
  "SELECT " & _
    "T1.販売ID, " & _
    "T1.商品ID, " & _
    "T2.商品名, " & _
    "T1.売上日, " & _
    "T1.数量, " & _
    "T2.定価 "

sql = sql & _
  "FROM 販売データ AS T1 " & _
    "INNER JOIN 商品マスター AS T2 " & _
    "ON T1.商品ID = T2.商品ID " & _
  "WHERE " & _
    "T1.売上日 BETWEEN #" & date1 & "# AND #" & date2 & "# " & _
    "AND T1.商品ID = '" & id & "' " & _
  "ORDER BY T1.売上日 DESC;"

イミディエイトウィンドウに出力して間違いを探す

SQLの記述は、1つ手を加えたらその都度動作確認をしていく癖をつけておくと、「どこで間違いが発生しているのかわからない!」という状況にハマりにくくなります。

しかしながら、ループ内で変数が意図しない値になるなど、不測の事態はいつでも起こり得ます。原因を調査するときは、合成した後のSQLがどんな形になっているかをイミディエイトウィンドウに出力してみると間違いが見つけやすくなります。

Dim sql As String
sql = "任意のSQL文"

Debug.Print sql

いかがだったでしょうか? 最初はちょっととっつきにくいと思いますが、変数を組み込んだSQLが扱えるようになるとできることがグッと広がります。ぜひチャレンジしてみてください!

さて、次回でこのコラムは最終回です。最後にExcelからAccessのデータベースを読み書きする方法をご紹介いたします。ご期待ください。

第1話「イベント」を使いこなそう
第2話 レイアウトで躓きやすいポイント
第3話 マクロとVBAの違い
第4話 DAOとADOの違い

リクルートスタッフィング

【第7話】ロジスティック回帰ってなあに?紹介編【漫画】未経験なのに、機械学習の仕事始めました

f:id:itstaffing:20210810141902j:plain
f:id:itstaffing:20210810141905j:plain
f:id:itstaffing:20210810141909j:plain
f:id:itstaffing:20210810141912j:plain
f:id:itstaffing:20210113102532j:plain
第5話第6話では、データの集計と可視化を実践しましたね!今回から、 いよいよ機械学習に入っていきましょう!

  未知の事象を予測する! ロジスティック回帰ってなあに?    

いよいよ機械学習について学んでいきましょう。機械学習には多種多様な手法がありますが、代表的な予測モデルの1つであるロジスティック回帰を紹介します。

ロジスティック回帰は、パターンAやパターンBのように2パターンに分けることができる事象に対して、AかBのどちらに属するかを予測できる手法です。

・雨が降るか否か
・試験に合格するか否か
・商品を購入するか否か
・犬か猫か

といったように、2パターンのどちらかに該当するかを予測します。

たとえば、過去に受験した模擬試験の結果を使うことで「本番の試験に合格するか否か」を予測できるかもしれません。

予測をするために利用するデータのことを、説明変数と言います。

複数の説明変数を使うことができ、国語の点数、数学の点数、社会の点数のように具体的な数字が入ります。また、「公立校に在籍している」「私立校に在籍している」という定性的なカテゴリーデータも説明変数に加えられます。

一方で、予測対象のデータもロジスティック回帰には必要で、これを目的変数と呼びます。

具体的には、

・試験に合格した場合は1
・不合格の場合は0

のように表現します。

予測に使う説明変数、予測対象の目的変数の2つを用いて、ロジスティック回帰という手法でデータの傾向を捉えます。このように傾向を捉えるのに利用するデータを訓練データ(または学習データ)と呼びます。

訓練データで傾向を捉えることができたら、試験をまだ受けていない生徒に対しても、模擬試験の結果から合否を予測できます。このとき、試験を受けてない生徒の予測に使うデータは、訓練データにおける説明変数と同じ項目です。

f:id:itstaffing:20210113102524j:plain
説明変数、目的変数、訓練データ……?次々に新しい単語が出てきて、混乱してきました。
f:id:itstaffing:20210113102532j:plain
ではここで一度、整理しておきましょう。

説明変数・・・予測をするために利用するデータのこと
例:国語の点数、数学の点数、社会の点数、公立校/私立高校在籍など、複数を活用することができる

目的変数・・・予測対象のデータのこと
例:試験に合格した場合は1、不合格の場合は0

訓練データ・・・傾向を捉えるのに利用するデータのこと
(ここでは、説明変数、目的変数のこと)

  ロジスティック回帰は、確率で予測できる!    

ロジスティック回帰では、予測結果を確率として考えることができます。「合格率70%です」「合格率20%です」というように、具体的な数値を得ることができるということです。

予測結果に対して、

・閾値を超えたら合格
・閾値未満であれば不合格

というように予測します。 (*区切りの基準になる値のこと)

単純に50%を超えたら合格と判定することもできますが、予測間違いに対する許容度で閾値を設定することもできます。

「予測で合格判定したにも関わらず、実際には不合格になってしまう生徒」を減らしたい場合は、閾値を70%のように高めに設定することで防げます。

f:id:itstaffing:20210113102532j:plain
ちなみに、機械学習ライブラリでは、パーセント表示ではなく0.7や0.2 のように小数点で結果を得るのが一般的です。

  ロジスティック回帰をもっと詳しく知りたい!    

では、ロジスティック回帰の詳細に踏み込んでいきます。

f:id:itstaffing:20210810141915j:plain

という式で表せます。

f(x)は出力結果で、0.7や0.2のように予測した合格率です。プログラムと同じでfという関数(ここではロジスティック回帰)に、変数x(国語の点数や数学の点数のような説明変数)を引数にとり、合格率の予測結果を返します。

ここでxは配列のように複数の値を持つことができ、具体的には国語の点数や数学の点数などになります。式を見ると分かるように、βとxは掛け算されています。βは回帰係数と呼ばれ、国語の点数や数学の点数が合否に与える影響を調整します。

たとえば、国語の点数に対するβは2で数学の点数に対するβは0.5である時、国語の点数が 30点で数学の点数が50点の生徒においてβ⧵*xは、2⧵*30+0.5⧵*50=85となります。

また、αは切片に相当し変数xの影響を受けません。x、α、βによる計算結果を0から1の連続値になるように変換する式がロジスティック回帰になります。

f(x)を計算するには予測対象となるデータxの他に、αやβが必要です。これらの値は、訓練データの傾向を捉えることで得られます。このことを「学習する」と呼びます。

ロジスティック回帰で学習したモデルを使うことで、未知の事象に対して予測できるようになるのです。

f:id:itstaffing:20210113102524j:plain
ロジスティック回帰がどのようなものかがわかりました!
f:id:itstaffing:20210113102532j:plain
次回はECサイトのデータに対して、ロジスティック回帰を適用し、ユーザーが翌月に購買するか否かを予測してみましょう。
f:id:itstaffing:20210113102524j:plain
次は、いよいよ実践編ですね!がんばるぞ~!

第1話 機械学習の仕事内容って?実はコードを書くだけじゃない!
第2話 人工知能、機械学習、ディープラーニングの違いとは?
第3話 機械学習の活用事例!建設機械や回転寿司屋でも活用されている!?
第4話 機械学習したいのにデータがない!?
第5話 集計と可視化:pandasでデータの加工をしてみよう
第6話 続!集計と可視化:Plotlyでデータをグラフ化して傾向をとらえよう

 

【筆者】
早川 敦士さん
株式会社FORCASの分析チームにてリーダーを務める傍らで、株式会社ホクソエムで執行役員として従事。新卒でリクルートコミュニケーションズに入社しWeb広告やマーケティングオートメーションなどのB2Cマーケティングを経験し、FORCASではB2Bマーケティングプラットフォームのデータ分析および開発を担当している。大学在学中に『データサイエンティスト養成読本』(技術評論社刊)を共著にて執筆。その後も『機械学習のための特徴量エンジニアリング』(オライリー・ジャパン刊)や『Pythonによるはじめての機械学習プログラミング』(技術評論社刊)などで執筆活動を続けている。国内最大級のR言語コミュニティであるJapan.Rを主催。Youtubeチャンネル『データサイエンティストgepuro』で動画を投稿。
・Twitterアカウント

湊川 あいさん
フリーランスのWebデザイナー・漫画家・イラストレーター。マンガと図解で、技術をわかりやすく伝えることが好き。 著書『わかばちゃんと学ぶ Git使い方入門』『わかばちゃんと学ぶ Googleアナリティクス』『わかばちゃんと学ぶ Webサイト制作の基本』『運用ちゃんと学ぶ システム運用の基本』『わかばちゃんと学ぶ サーバー監視』が発売中のほか、マンガでわかるGit・マンガでわかるDocker・マンガでわかるRuby・マンガでわかるScrapbox・マンガでわかるLINE Clova開発・マンガでわかる衛星データ活用といった分野横断的なコンテンツを展開している。
・Amazon著者ページ
・Twitterアカウント

  わかばちゃんが登場する書籍いろいろ   

f:id:itstaffing:20210113102514j:plain  f:id:itstaffing:20210113102516j:plain
 
f:id:itstaffing:20210113102519j:plain  f:id:itstaffing:20210113102521j:plain


わかばちゃんと学ぶシリーズ(Amazonページ)

第4話 DAOとADOの違い【連載】実務で使えるAccessのコツ

f:id:itstaffing:20210803162809j:plain

データベース管理ソフトウェア、Accessを実務で使うときに「知っているとちょっと便利なコツ」について紹介するこのコラム。今回は、AccessVBAを使う方なら一度は考えるDAOとADOの違いと使い分けについて、記事公開時点(2021年8月)の最新の情報を踏まえて解説します。あなたのAccess力アップに役立ちますように。

前回のコラムを見逃した方はこちらからご覧ください。
第3話 マクロとVBAの違い

【筆者】今村 ゆうこさん
【筆者】今村 ゆうこさん
地方の非IT系企業に勤務し、業務アプリケーション開発を行うプログラマー。著書のイラストや図解も手掛けている。大学卒業後出身地へUターン就職し、2度の産休/育休を経て現在は小学生と保育園児の2人のこどもを抱えるワーキングマザー。主にVBAを使ってデータベースの情報を加工/展開するアプリを開発するほか、WordPressを使った個人ブログ(https://ateitexe.com/)で技術情報を発信している。著書に『Accessデータベース 本格作成入門』『Excel & Access連携 実践ガイド』(技術評論社)など。

リクルートスタッフィング

  DAOとADOってなんのこと?    

AccessVBAを使ってデータベースを操作しようとすると、DAOやADOという単語を耳にしませんか? これは一体、何なのでしょうか?

ざっくり言うと、どちらもデータベースへ接続・操作する手段のことです。Accessでは、どちらも利用することができます。

f:id:itstaffing:20210803162811j:plain

今回は、この2つについて学んでいきましょう。

  どう違うの?    

この2つは略称で、正式名称は以下のとおりです。

・DAO … Data Access Objects
・ADO … ActiveX Data Object

DAOはAccessで使用されるデータベースに特化した手段です。それに対してADOは、Accessをはじめ各社の主要なデータベースでも幅広く使える接続手段です。

DAOに関しては、Access開発当初からずっと使われてきた技術だったものの、Access2000よりADOが使えるようになったことで、一時期はADOが推奨される風潮があったようです。

しかし、Access2007よりDAOが新しくなったことから、現在では「古い手段」というイメージは払拭されています。

  どちらを使うべき?    

この記事が公開される2021年8月現在では、Accessにおいて、DAO/ADOどちらを使っても大きな差はありません。

DAOは元々Accessに特化した技術なのでADOよりも高速と言われていますが、パソコンのスペックもどんどん上がっているので、体感できるほどの差にはなりにくいと思われます。

Accessのみで完結するシステムならばDAO、Access以外でデータベースを使う・または今後その可能性がある場合はADO、という判断をしてもよいのではないでしょうか。

  テンプレート化して使うのがおすすめ    

データベースを操作するには、SQLというデータベースに問い合わせを行うための言語を使います。

VBAからデータベースを操作する際、SQLは目的によって内容を変えなくてはなりませんが、そのSQLを「処理する部分」は一定です。DAO/ADOそれぞれ、処理部分のみ切り分けたテンプレートを用意しておけば、SQLのみの変更でかんたんに利用することができます。

f:id:itstaffing:20210803162814j:plain

ただし、SQLでの問い合わせには大きく分けて「データベースから任意のレコードを抽出する」働きと「データベースの内容を変更する」働きがあるので、VBAで「処理する部分」はDAO/ADOそれぞれに、2種類ずつ必要になります。

特に「任意のレコードを抽出する」場合、レコードセットというオブジェクトが登場します。これは、データベースのレコードが集まった表形式のデータ構造で、Accessでテーブルやクエリをデータシートビューで開いた時と同じイメージのものをVBA上で保持、扱うことができます。

後述のコードで、任意のレコードの集合を「レコードセット」という形で取得し、それを展開する記述がありますので、注目してみてください。

それではここからはDAO/ADOのテンプレートとして使える、SQLを「処理する部分」のAccessVBAをご紹介します。任意のテーブルが存在しているaccdbファイルを開いた状態からAlt+F11キーでVBE(Visual Basic Editor)を開きます。

「挿入」→「標準モジュール」を作成して、そこへコードをコピー&ペーストしてお使いください。

f:id:itstaffing:20210803162751j:plain

  DAOのテンプレート    

それではまず、DAOを利用する方法から紹介します。

DAOは2021年8月現在ではAccessの規定のデータベース接続方法と考えられるため、標準で「ツール」→「参照設定」の「Microsoft Office XX.X Access database engine Object Library」にチェックが付いています。この設定でDAO.DatabaseやDAO.Recordsetなどのオブジェクトが利用できます。

f:id:itstaffing:20210803162755j:plain

以下はレコードの抽出で使用するテンプレートです。引数のSQLによって抽出されたレコードを2次元配列へ格納して返します。

Public Function getRsInArray_DAO(ByVal sql As String) As Variant
'## レコードセットを配列へ入れて返す

On Error GoTo Err_Handler 'エラーが起きたら「ErrorHandler」にジャンプする指示

Dim db As DAO.Database 'DAOデータベース用オブジェクトの宣言
Set db = CurrentDb '現在開いているデータベースへ接続

'レコードセットのオープン
Dim rs As DAO.Recordset 'DAOレコードセットオブジェクト変数宣言
Set rs = db.OpenRecordset(sql) 'カーソルタイプを指定してレコードセットを開く

'空だったら最終処理へ
If rs Is Nothing Or (rs.BOF And rs.EOF) Then 'レコードセットやレコードが存在しなかった場合
  getRsInArray_DAO = Array() '空の配列を返す
  GoTo Finally '最終処理へジャンプ
End If

'レコード数を取得
Dim recordCount As Long
rs.MoveLast
recordCount = rs.recordCount
rs.MoveFirst

'配列に展開
Dim ary() As Variant
ReDim ary(recordCount - 1, rs.Fields.Count - 1) '配列の要素数を定義
Dim rsNum As Long '縦要素(レコードセット)の繰り返し用変数
Dim fldNum As Long '横要素(フィールド)の繰り返し用変数
Do Until rs.EOF 'レコードセットが終了するまで処理を繰り返す
For fldNum = 0 To rs.Fields.Count - 1 'フィールドの数だけ繰り返す
  ary(rsNum, fldNum) = rs(fldNum) '配列に格納
Next fldNum
rsNum = rsNum + 1 '縦要素をカウントアップする
rs.MoveNext '次のレコードに移動する
Loop

getRsInArray_DAO = ary '配列を返す

GoTo Finally '正常に終了したら最終処理へジャンプ

Err_Handler: '例外処理
  Dim msgTxt As String
  msgTxt = "Error #: " & Err.Number & vbNewLine & vbNewLine & Err.Description 'エラーメッセージが入る
  MsgBox msgTxt, vbOKOnly + vbCritical, "エラー" 'メッセージ出力
  getRsInArray_DAO = Array() '空の配列を返す

Finally: '最終処理
If Not rs Is Nothing Then 'レコードセットオブジェクトが存在している場合のみ
  rs.Close 'レコードを閉じる
  Set rs = Nothing
End If
If Not db Is Nothing Then '接続解除
  db.Close
  Set db = Nothing
End If
End Function

以下はレコードの挿入・更新・削除で使用するテンプレートです。引数のコレクション内に格納されているすべてのSQLを実行し、結果をTrue/Falseで返します。トランザクションが実装されているので、途中でエラーが発生した場合は処理されません。

Public Function tryExecute_DAO(ByVal sqlList As Collection) As Boolean
'## SQLの実行
On Error GoTo ErrorHandler 'エラーが起きたら「ErrorHandler」にジャンプする指示

Dim ws As DAO.Workspace 'トランザクション用オブジェクト
Set ws = DBEngine(0)

Dim db As DAO.Database 'DAOデータベース用オブジェクトの宣言
Set db = CurrentDb '現在開いているデータベースへ接続

ws.BeginTrans 'トランザクション開始

'実行
Dim sql As Variant
For Each sql In sqlList 'SQL文リストをループ
  db.Execute sql '1行ずつ実行
Next sql

ws.CommitTrans '確定

tryExecute_DAO = True '成功だった場合、関数の結果にTrueを入れる
GoTo Finally '正常に終了したら最終処理へジャンプ

ErrorHandler: '例外処理(エラーが起きたらここへジャンプ)
ws.Rollback '元の状態へ戻す
Dim msgTxt As String
msgTxt = "Error #: " & Err.Number & vbNewLine & vbNewLine & Err.Description 'エラーメッセージ格納
MsgBox msgTxt, vbOKOnly + vbCritical, "エラー" 'メッセージ出力

Finally: '最終処理
If Not db Is Nothing Then '接続解除
  db.Close
  Set db = Nothing
End If
If Not ws Is Nothing Then 'トランザクション用のオブジェクトを破棄
  ws.Close
  Set ws = Nothing
End If
End Function

  ADOのテンプレート    

続いてADOを利用する方法です。

DAOの場合、「ツール」→「参照設定」の「Microsoft Office XX.X Access database engine Object Library」を利用しましたが、ADOで同様にするには、「Microsoft ActiveX Data Objects X.X Library」を利用します。パソコン環境によってバージョンが複数存在する可能性がありますが、数字の大きなものがよいでしょう。

f:id:itstaffing:20210803162759j:plain

ただし、このライブラリはデフォルトでチェックがオフになっているので、VBAを組み込んだAccessファイルを別のパソコンで使うときに、参照設定でチェックを入れ直さないとエラーになってしまいます。

ライブラリを使ったほうが開発時は便利なのですが、運用時にうっかりエラーを引き起こす可能性もあるので、今回は参照設定の「Microsoft ActiveX Data Objects X.X Library」にチェックを入れずにADOを利用するコードをご紹介します。

以下はレコードの抽出で使用するテンプレートです。引数のSQLによって抽出されたレコードを2次元配列へ格納して返します。

Public Function getRsInArray_ADO(ByVal sql As String) As Variant
'## レコードセットを配列へ入れて返す

Const adStateOpen = 1 'レコードセットが開いている場合の設定値
Const adOpenKeyset = 1 'レコードセットカーソルタイプの設定値

On Error GoTo Err_Handler 'エラーが起きたら「ErrorHandler」にジャンプする指示

Dim cn As Object 'ADOコネクション用オブジェクトの宣言
Set cn = CurrentProject.Connection '現在開いているデータベースへ接続

'レコードセットのオープン

Dim rs As Object 'レコードセット用変数宣言
Set rs = CreateObject("ADODB.RecordSet") 'ADOレコードセットオブジェクトを作成
rs.Open sql, cn, adOpenKeyset 'カーソルタイプを指定してレコードセットを開く

'空だったら最終処理へ

If rs Is Nothing Or (rs.BOF And rs.EOF) Then 'レコードセットやレコードが存在しなかった場合
  getRsInArray_ADO = Array() '空の配列を返す
  GoTo Finally '最終処理へジャンプ
End If

'配列に展開
Dim ary() As Variant
ReDim ary(rs.recordCount - 1, rs.Fields.Count - 1) '配列の要素数を定義
Dim rsNum As Long '縦要素(レコードセット)の繰り返し用変数
Dim fldNum As Long '横要素(フィールド)の繰り返し用変数
Do Until rs.EOF 'レコードセットが終了するまで処理を繰り返す
For fldNum = 0 To rs.Fields.Count - 1 'フィールドの数だけ繰り返す
  ary(rsNum, fldNum) = rs(fldNum) '配列に格納
Next fldNum
rsNum = rsNum + 1 '縦要素をカウントアップする
rs.MoveNext '次のレコードに移動する
Loop

getRsInArray_ADO = ary '配列を返す

GoTo Finally '正常に終了したら最終処理へジャンプ

Err_Handler: '例外処理
  Dim msgTxt As String
  msgTxt = "Error #: " & Err.Number & vbNewLine & vbNewLine & Err.Description 'エラーメッセージが入る
  MsgBox msgTxt, vbOKOnly + vbCritical, "エラー" 'メッセージ出力
  getRsInArray_ADO = Array() '空の配列を返す

Finally: '最終処理
If Not rs Is Nothing Then 'レコードセットオブジェクトが存在している場合のみ
  If rs.State = adStateOpen Then rs.Close 'レコードセットが開いていたら閉じる
  Set rs = Nothing
End If
If Not cn Is Nothing Then
  cn.Close '接続解除
  Set cn = Nothing
End If
End Function

以下はレコードの挿入・更新・削除で使用するテンプレートです。引数のコレクション内に格納されているすべてのSQLを実行します。トランザクションが実装されているので、途中でエラーが発生した場合は処理されません。

Public Function tryExecute_ADO(ByVal sqlList As Collection) As Boolean
'## SQLの実行

On Error GoTo ErrorHandler 'エラーが起きたら「ErrorHandler」にジャンプする指示

Dim cn As Object 'ADOコネクション用オブジェクトの宣言
Set cn = CurrentProject.Connection '現在開いているデータベースへ接続

cn.BeginTrans 'トランザクション開始

'実行
Dim sql As Variant
For Each sql In sqlList 'SQL文リストをループ
  cn.Execute sql '1行ずつ実行
Next sql

cn.CommitTrans '確定

tryExecute_ADO = True '成功だった場合、関数の結果にTrueを入れる
GoTo Finally '正常に終了したら最終処理へジャンプ

ErrorHandler: '例外処理(エラーが起きたらここへジャンプ)
cn.RollbackTrans '元の状態へ戻す
Dim msgTxt As String
msgTxt = "Error #: " & Err.Number & vbNewLine & vbNewLine & Err.Description 'エラーメッセージ格納
MsgBox msgTxt, vbOKOnly + vbCritical, "エラー" 'メッセージ出力

Finally: '最終処理
If Not cn Is Nothing Then
  cn.Close '接続解除
  Set cn = Nothing
End If
End Function

  テンプレートの利用(レコードの抽出)    

SQLのSELECT構文を作成し、引数としてテンプレートへ渡して結果のレコードセットを二次配列の形で受け取るコードがこちらです。

SELECT構文の基本形は以下のようなものです。

SELECT フィールド1, フィールド2 FROM テーブル WHERE 条件;

Public Sub selectRecord()
'## レコードの抽出

'SQL作成
Dim sql As String
sql = "任意のSELECT構文"

'レコードセットを配列で取得
Dim ary() As Variant
ary = getRsInArray_DAO(sql) '←DAOを利用する場合
'ary = getRsInArray_ADO(sql) '←ADOを利用する場合

'空の配列だったら終了

If isEmptyArray(ary) Then Exit Sub

'中身の確認
Dim rsCount As Long
rsCount = UBound(ary, 1) '第1要素(レコード)数
Dim fldCount As Long
fldCount = UBound(ary, 2) '第2要素(フィールド)数
Dim x As Long, y As Long
For x = 0 To rsCount
For y = 0 To fldCount
  Debug.Print ary(x, y), ; 'イミディエイトウィンドウに出力
Next y
Debug.Print "" 'イミディエイトウィンドウの改行
Next x
End Sub

取得部分は、DAO/ADOどちらか一方をお使いください。

配列を受け取った後、中身を確認する前に配列が空かどうかの判定も必要なので、以下の関数も任意のモジュールに追記してください。

Public Function isEmptyArray(ByVal tgtAry As Variant) As Boolean
'## 配列が空か判定

On Error GoTo Err_Handler 'エラーが起きたら「ErrorHandler」にジャンプする指示
If UBound(tgtAry) >= 0 Then '配列に要素が存在したら
  Exit Function  '終了(Falseを返す)
End If

Err_Handler:  'エラーもしくは要素が存在しない場合
  isEmptyArray = True  'Trueを返す
End Function

コードを実行すると、SELECT構文で抽出した配列の中身がVBEのイミディエイトウィンドウ(表示されていない場合は「表示」→「イミディエイトウィンドウ」)で確認できます。

f:id:itstaffing:20210803162804j:plain

取得した配列は、そのままコンボボックスやリストボックスのソースにセットすることができるほか、レコードの値を非連結で扱えるのでさまざまな実装に役立ちます。

  テンプレートの利用(レコードの挿入・更新・削除)    

SQLのINSERT/UPDATE/DELETE構文をコレクションに格納し、引数としてテンプレートへ渡して結果をTrue/Falseの形で受け取るコードがこちらです。

それぞれの構文の基本形は以下のようなものです。

・INSERT INTO テーブル(フィールド1, フィールド2) VALUES(値1, 値2);
・UPDATE 
テーブル SET フィールド1=値1, フィールド2=値2  WHERE  条件;
・DELETE FROM
 テーブル  WHERE 条件;

複数のSQL文に対応するためコレクションに格納していますが、格納されるSQL文が1つでも動作します。数が多い場合はDoやForなどのループを使ってSQLを作成&コレクションへ格納していくとスマートです。

Public Sub executeSQL()
'## レコードの挿入・更新・削除

'コレクションの作成
Dim sqlList As Collection
Set sqlList = New Collection

'SQL作成
Dim sql As String 'SQL文用変数の宣言

sql = "任意のINSERT/UPDATE/DELETE構文" '1つ目のSQL文
sqlList.Add sql 'コレクションへ追加

sql = "任意のINSERT/UPDATE/DELETE構文" '2つ目のSQL文
sqlList.Add sql 'コレクションへ追加

'実行
If tryExecute_DAO(sqlList) Then '処理が成功した場合 '←DAOを利用する場合
'If tryExecute_ADO(sqlList) Then '処理が成功した場合 '←ADOを利用する場合

  MsgBox "正常に終了しました", vbOKCancel + vbInformation, "終了" '終了メッセージ
End If
End Sub

実行部分は、DAO/ADOどちらか一方をお使いください。

実行すると、問題が起きた場合は処理されずにエラー内容が、問題なく処理が終了した場合は終了メッセージが表示されます。

f:id:itstaffing:20210803162748j:plain

いかがだったでしょうか?次回はVBAでSQL文を扱う際のコツやポイントについてです。1行で済むような短い文ならば理解しやすいのですが、可読性のために改行したい、変数を使いたい、などの実用的なSQL組み込みのためのヒントをご紹介しますので、ぜひご期待ください。

 

第1話「イベント」を使いこなそう
第2話 レイアウトで躓きやすいポイント
第3話 マクロとVBAの違い

リクルートスタッフィング

第3話 マクロとVBAの違い【連載】実務で使えるAccessのコツ

f:id:itstaffing:20210702175107j:plain

データベース管理ソフトウェア、Accessを実務で使うときに「知っているとちょっと便利なコツ」について紹介するこのコラム。今回は意外と曖昧になりがちな、マクロとVBAの違いを解説します。特に間違えやすい、Excelのマクロ/VBAとの違い、説明できますか?あなたのAccess力アップに役立ちますように。

前回のコラムを見逃した方はこちらからご覧ください。
第2話 レイアウトで躓きやすいポイント

【筆者】今村 ゆうこさん
【筆者】今村 ゆうこさん
地方の非IT系企業に勤務し、業務アプリケーション開発を行うプログラマー。著書のイラストや図解も手掛けている。大学卒業後出身地へUターン就職し、2度の産休/育休を経て現在は小学生と保育園児の2人のこどもを抱えるワーキングマザー。主にVBAを使ってデータベースの情報を加工/展開するアプリを開発するほか、WordPressを使った個人ブログ(https://ateitexe.com/)で技術情報を発信している。著書に『Accessデータベース 本格作成入門』『Excel & Access連携 実践ガイド』(技術評論社)など。

  マクロ?VBA?曖昧なまま使ってる??    

このコラムをお読みの方ならば、マクロ/VBAという単語を耳にしたことはあると思います。しかし、具体的にその2つがどう違うかというと、少々難問かもしれません。意外と曖昧なまま使っている方も多いのではないでしょうか?

f:id:itstaffing:20210702174914j:plain

今回はこの疑問を、Accessを含めたMicrosoft Officeシリーズ全体の視点から解説します。

  マクロとVBAの違い    

まずは、言葉のおおまかな意味を捉えましょう。

マクロとは、ざっくり言うと「自動化された操作を制御する機能」のことです。これはMicrosoft Officeシリーズに限らず、さまざまなIT分野に存在する概念です。使われる場所によって少しずつ意味が異なるので、ここではおおまかな意味として「機能」と説明します。

そして、VBAとは「Visual Basic for Application というプログラム言語」のことで、Microsoft社が開発した汎用プログラミング言語であるVisual Basicを、Office製品向け(for Application)に派生させた言語です。この言語のおかげで、Officeアプリケーションでは他のセッティングをせずとも手軽にプログラミングが始められます。

Microsoft Officeシリーズにおいて、マクロとは自動化された機能を指す言葉であり、VBAはそれを実現するために使われている手段です。 ただし、本来VBAはプログラミング言語の1つですが、「VBAでプログラミングされた機能」のことも広義でVBAと呼ばれる傾向があるため、この2つの違いが曖昧になってしまうのではないかと思います。

  OfficeシリーズにおけるマクロとVBA    

それではここで、Officeの代表的なアプリケーションで「マクロ」がどのように使われているかを見てみましょう。以下の図はExcelを起動したところです。マクロは「開発」タブから扱うので、表示されていない場合は「ファイル」→「その他」→「オプション」から「リボンのユーザー設定」の「開発」にチェックを入れます。

f:id:itstaffing:20210702174958j:plain

まずは、「マクロの記録」を試してみましょう。マクロ名は後で実行するときの選択肢になるので、機能の内容を簡潔に表現すると良いでしょう。

f:id:itstaffing:20210702175002j:plain

すると、リボンの「マクロの記録」だった部分が「記録終了」に変化します。このボタンをクリックするまで、Excel上で行った操作は記録されます。選択範囲に罫線をつける操作をして、「記録終了」させます。

f:id:itstaffing:20210702175006j:plain

いま記録したマクロを実行してみましょう。いったん罫線をクリアして、「開発」タブの「マクロ」をクリック(またはAlt+F8キーを押下)し、さきほど設定したマクロ名を選択して「実行」ボタンをクリックします。

f:id:itstaffing:20210702175009j:plain

記録された操作が再現されました。

f:id:itstaffing:20210702175013j:plain

前述したように、この記録と実行はVBA言語で行われています。しかし「マクロの記録」では、ここまでの一連の流れのように、VBAの言語を一度も目にすることなく機能を作成、利用することができます。

それでは、今度は記録されたマクロの実体を見てみましょう。「開発」タブの「Visual Basic」をクリック(またはAlt+F11キーを押下)します。

f:id:itstaffing:20210702175018j:plain

すると、以下のような画面が開きます。これはVisual Basic Editorと呼ばれる、VBAでプログラミングを行うための画面です。

左上の領域(プロジェクトエクスプローラー)の「標準モジュール」内の「Module1」をダブルクリックすると、その中身が見られます。これが、さきほど「マクロの記録」によって自動記述されたVBAのコード(命令のための記述)です。さきほど実行されたマクロの実体はこのコードです。

f:id:itstaffing:20210702175023j:plain

なお、Microsoft Officeに限って言えば、マクロはVBA言語によって作成されているので根本は同じものなのですが、「マクロの記録」などで自動記録した機能を「マクロ」、VBE(VBA編集画面)にて自分でコードを打ち込んで作成した機能を「VBA」と呼ぶ傾向があるようです。

また、ほかの代表的なOfficeアプリケーションのなかでは、Wordには「マクロの記録」が搭載されていますが、PowerPointやOutlookには搭載されていません。

f:id:itstaffing:20210702175030j:plain

  AccessにおけるマクロとVBA    

ExcelなどではVBAを(直接入力また自動で)記述して作った機能がマクロとなりますが、Accessにおいては異なります。Accessでは、ほかのOfficeアプリケーションとは違った、独立した意味での「マクロ」が存在するのです。

Accessのリボンを見てみましょう。「開発」タブがなく、「作成」タブの中に「マクロ」があります。

f:id:itstaffing:20210702175034j:plain

クリックしてみると、以下のような「マクロツール」という画面が表示されます。この画面では、動作や対象を選択して組み立てて、それを実行できる機能を作ることができます。

f:id:itstaffing:20210702175038j:plain

Accessにおける「マクロ」とは、「選択・クリック・ドラッグなどのマウス操作で直感的に扱えるプログラミング方法」です。そして、その実体はVBA言語で制御されています。

これはちょうど、「クエリ」機能をクリック&ドラッグで組み立てることができる「デザインビュー」が、その実体はSQL言語で制御されている関係性に似ています(「SQLビュー」でコードを見ることができます)。

f:id:itstaffing:20210702175045j:plain

なお、マクロツールにはクエリのSQLビューのようなコードで表示させる画面はありませんが、作成したマクロをVBAコードに変換する機能を持っています。

f:id:itstaffing:20210702175051j:plain

  できることに違いはあるの?    

ここからはAccessにおいてのマクロとVBAの関係を前提にお話していきます。

マクロツールにてマウス操作で作成できる機能と、VBEでコードを直接入力してVBA言語で作成できる機能、この2つに差はあるのでしょうか?

マクロツールに用意されているアクションは、VBAを元に作られているので、VBAで作成できる大抵のことはマクロでも再現できます。

ただしマクロではAccess単体で実現できる範囲内にとどまり、VBAではアプリケーションの枠を超えた範囲まで制御することができます。たとえばAccessに書いたVBAでExcelを操作するなどのOffice間連携や、フォルダ作成・ファイル名変更などのOS上の操作も可能です。

  どちらのほうが難しいの?    

マクロツールでのプログラミングは、かんたんな操作ならVBAの書き方を知らなくても作ることができるので、敷居が低くチャレンジはしやすいでしょう。下図のように日本語で書かれているので、非プログラマの方でも「ここをこうしたらいいのかな」という検討がつきやすく、修正も比較的難易度が低くなります。

f:id:itstaffing:20210702175055j:plain

ただし、マクロにも文法が存在していて、その理解にはVBAの知識が必要です。

「この場合はこの動き(条件分岐)」「エラーが起きたらこの場所へジャンプ(エラートラップ)」などの複雑な動きをさせようと思うと下図のようになりますが、「VBAではこのように書く」という前提知識がないとマクロを組み立てるのは難しいと思われます。

f:id:itstaffing:20210702175101j:plain

VBAの文法を知っていればそれをマクロで再現することは可能ですが、結局は修正の際にもVBAの知識が必要になりますし、いったんVBAの文法を考えてマクロで書くという手間もプラスされます。したがって、ある程度以上複雑なマクロは、かえって難易度が高くなってしまう可能性があることも留意しておきましょう。

  どちらを使うべき?    

一部VBAにしかできないことを除けば、大抵のことはどちらも実装できるので、プログラミングを設定する規模と、その後のメンテナンスは誰がどの頻度で行うのかということを踏まえて選択するのが良いのでは、というのが筆者の私見です。

動作する要素が少なくて、非プログラマもメンテナンスする可能性があるのなら、マクロを使ったほうが作成も修正も難易度が低くなります。ある程度複雑な内容で、マクロを組み立てるのにVBAの知識が必要なレベルならば、最初からVBAで書いてしまったほうが後々のメンテナンスは楽かもしれません。

ただし、同じファイル内でこの機能はマクロ、この機能はVBA、と混在しているとそれはそれで全体像の把握が難しくなるので、チームで使いやすい形を相談して、そのルールや設計図をきちんと記録し、共有できる仕組みを作っておくのがよいでしょう。

次回はデータベースとVBAの接続手段であるADOとDAOの違いについて解説します。Accessではどちらも使用できるので、どちらを使うべきか迷う方は、ぜひ参考にしてください。

AccessのマクロとVBAの違いはわかりましたか。Excelをメインで使われている方だとどうしても間違えがちなので、しっかり押さえておきましょう。

リクルートスタッフィングでは、エンジニアのスキルアップを応援するため、本記事以外にも登録者限定の記事やイベントなどを配信しています。 リクルートスタッフィングでの就業に関わらず使えるものですので、これを機にお役立てください。

リクルートスタッフィング

第1話「イベント」を使いこなそう
第2話 レイアウトで躓きやすいポイント

【第6話】続!集計と可視化:Plotlyでデータをグラフ化して傾向をとらえよう【漫画】未経験なのに、機械学習の仕事始めました

f:id:itstaffing:20210628142230j:plain

前回の続き:第5話 集計と可視化:pandasでデータの加工をしてみよう

f:id:itstaffing:20210628142233j:plain
f:id:itstaffing:20210628142236j:plain
f:id:itstaffing:20210113102532j:plain
第5話では、pandasを使ってデータを加工する方法を紹介しました。今回は、加工したデータをグラフにすることで、データの傾向をとらえてみましょう!

  今回のサンプルコード

サンプルコードは、こちらです。

  処理を単純化するために、最初におかしな値を除外する

第5話で、わかばちゃんは Quantity(購入量) にマイナス値を見つけました。その理由は、返品処理のせいでしたね。

f:id:itstaffing:20210628142203j:plain
f:id:itstaffing:20210628142206j:plain

Quantity(購入量)だけでなく、UnitPrice(単価)も原因不明のマイナス値がありました。データ加工の処理を単純化するために、Quantity(購入量)及び、UnitPrice(単価)がマイナス値のデータを事前に削除しましょう。

※今回のデータセット(サンプルデータ)では、マイナス値のQuantityは返品処理ではありましたが、データセットに記録されていない購入履歴もあるようです(詳細は省略します)

  Plotlyを使ってユーザーの購買履歴を折れ線グラフにしてみよう

Plotlyのインストール:pip install plotly
Plotlyはオープンソースのデータ可視化ライブラリで、Pythonだけでなく、R、JavaScript、MATLABなどでも使うことができます。

まずは、ユーザーの購買履歴を折れ線グラフで可視化してみましょう。データの 1 行ごとに行動が記録されており、これを追跡することでユーザーの活動を追体験することができます。

アクセスログを分析すると、どの商品を購入するか迷った形跡を追うこともできます。購買履歴からは、誰が・いつ・何を購入したかがわかります。

Plotlyでグラフを描くために、plotly.graph_objectsを読み込んでgoという名前をつけておきます。go.Figureでグラフ描画の準備、go.Scatterでグラフの作成です。

データ上では、8月30日に初めて商品を購入しており12月5日に1日当たりの購入金額が最大になっています。

Scatterにはオプションがあり、mode='lines'でデータ点をプロットせずに線のみを描画することができます。
f:id:itstaffing:20210628142208j:plain
```
import plotly.graph_objects as go
fig = go.Figure(data=go.Scatter(x=購入日時のデータ, y=購入量のデータ))
fig.show()
```

  ユーザーの居住地を円グラフにしてみよう

次に、ユーザーの居住地を円グラフで可視化してみましょう。円グラフではマイナーな項目全てを描画すると可読性が下がってしまうので「その他」でまとめるのがオススメです。

f:id:itstaffing:20210628142211j:plain

このデータセットはイギリスのECサイトであることから、約90%がイギリス国内からの購入は自然な結果ですね。約10%は他国に居住しているユーザーが購入していることが、興味深い点です。

円グラフの描画は、plotly.expressを読み込んで利用すると簡単です。
```
import plotly.express as px
fig = px.pie(データ, values='count', names='Country')
fig.show()
```

  購入実績の平均を、棒グラフで比較してみよう

前回実施したRFM分析(Recency:直近いつ、Frequency:頻度、Monetary:購入金額)における、Recencyの平均と翌月の購入実績の有無を用いて棒グラフを描いてみましょう。

翌月の購入実績があるユーザーは、Recency平均が約61日であるのに対して、翌月に購入していないユーザーのRecency平均は約110日。直近に購入しているユーザーほど、翌月にも購入している傾向があることがわかります。

棒グラフの作成には、go.Barを使います。
is_visit=Falseが購入実績のないユーザー、is_visit=Trueが購入実績のあるユーザーです。

f:id:itstaffing:20210628142213j:plain
```
fig = go.Figure(data=[ go.Bar(name='is_visit=False', x=["recency"], y=翌月の購入実績がないユーザーのRecencyの平均),
go.Bar(name='is_visit=True', x=["recency"], y=翌月の購入実績があるユーザーのRecencyの平均)
])
fig.update_layout(barmode='group')
fig.show()
```

Recency について、より詳しくデータを眺めていきましょう。

ヒストグラムを使うことで、データがどのように分布しているかを可視化することができます。分布を見ることでデータの全体像を捉えることができ、Recencyが小さいユーザー(直近に購入実績があるユーザー)が多いのか少ないのか、均等に散らばっているのか偏っているのか、ひと目でわかります。

このヒストグラムからはRecencyが小さいユーザーが多く、大きくなるに従ってユーザーが減っていくことがわかります。ロングテールな分布と呼ばれる形をしています。

ロングテールに分布するデータを平均値で集計すると、大きな値の影響を強く受けるので一般的な感覚からズレた結果になることが多いです。年収の平均値が一般的な感覚よりも高くなるのが有名な例です。描画にはpx.histgramを使います。
f:id:itstaffing:20210628142217j:plain
```
fig = px.histogram(データ, x="recency")
fig.show()
```

翌月の購入実績の有無で棒グラフを作って比較したように、ヒストグラムを2つ並べることも有用です。2つのヒストグラムを縦に並べて比較することによって、平均値で比較する棒グラフよりも細かい情報を得ることができます。

翌月に購入した青色のヒストグラムと購入していない赤色のヒストグラムを比較すると、青色の方が全体的に左側にデータが分布していて直近に商品を購入していることがわかります。

翌月に購入実績がないユーザーであっても直近30日以内に購入した人が全体の約28%もいます。直近に購入しているほど翌月も購入しやすい傾向はありますが、必ず購入するとは限らないことがわかります。

f:id:itstaffing:20210628142220j:plain

  散布図で外れ値をチェックしよう

次は散布図を描きます。px.scatterもしくはgo.Scatterを使います。px.scatterを用いる方が短いコードで描画可能です。

散布図はデータの傾向を捉えるときの基本的なグラフです。
横軸が Recency(直近いつ)、縦軸が Frequency(頻度)としています。
f:id:itstaffing:20210628142224j:plain
f:id:itstaffing:20210113102524j:plain
右端に1つだけ、ぴょこんと飛び出したデータがあるね!?Recencyは長いけど、Frequencyは高い。特殊なお客様だ。
f:id:itstaffing:20210113102532j:plain
そうですね!これは平均値では見えなかった事実ですね。一部のユーザーは高頻度で商品を購入していることがわかります。このような、他のデータとは明らかに傾向が異なるデータのことを 外れ値 と呼びます。

外れ値となるようなユーザーを細かく観測することで、サービスの珍しい使い方を発見できるかもしれません。場合によっては不適切な使い方をしているユーザーが外れ値となることもあります。

```
fig = px.scatter(x=Recencyデータ, y=Frequencyデータ)
fig.show()
```

  バブルチャートを描いてみよう

最後に、複雑なグラフを描いてみましょう。

・横軸にRecency(直近いつ)
・縦軸にFrequency(頻度)
・データ点の○の大きさでMonetary(購入金額)
・色で翌月に購入したか(翌月購入は赤色、未購入は青色)

雑なグラフになるほど、グラフが伝えるメッセージが不明瞭になりますが、データの傾向を捉えたい探索的な分析には有効です。

f:id:itstaffing:20210628142228j:plain
f:id:itstaffing:20210113102532j:plain
Plotlyを用いることで、さまざまなグラフを作成できます。より詳しい使い方を知りたい方は、https://plotly.com/python/をチェックしてくださいね。次回は、予測モデルとして有名なロジスティック回帰について学んでいきましょう!

第1話 機械学習の仕事内容って?実はコードを書くだけじゃない!
第2話 人工知能、機械学習、ディープラーニングの違いとは?
第3話 機械学習の活用事例!建設機械や回転寿司屋でも活用されている!?
第4話 機械学習したいのにデータがない!?
第5話 集計と可視化:pandasでデータの加工をしてみよう

【筆者】
早川 敦士さん
株式会社FORCASの分析チームにてリーダーを務める傍らで、株式会社ホクソエムで執行役員として従事。新卒でリクルートコミュニケーションズに入社しWeb広告やマーケティングオートメーションなどのB2Cマーケティングを経験し、FORCASではB2Bマーケティングプラットフォームのデータ分析および開発を担当している。大学在学中に『データサイエンティスト養成読本』(技術評論社刊)を共著にて執筆。その後も『機械学習のための特徴量エンジニアリング』(オライリー・ジャパン刊)や『Pythonによるはじめての機械学習プログラミング』(技術評論社刊)などで執筆活動を続けている。国内最大級のR言語コミュニティであるJapan.Rを主催。Youtubeチャンネル『データサイエンティストgepuro』で動画を投稿。
・Twitterアカウント

湊川 あいさん
フリーランスのWebデザイナー・漫画家・イラストレーター。マンガと図解で、技術をわかりやすく伝えることが好き。 著書『わかばちゃんと学ぶ Git使い方入門』『わかばちゃんと学ぶ Googleアナリティクス』『わかばちゃんと学ぶ Webサイト制作の基本』『運用ちゃんと学ぶ システム運用の基本』『わかばちゃんと学ぶ サーバー監視』が発売中のほか、マンガでわかるGit・マンガでわかるDocker・マンガでわかるRuby・マンガでわかるScrapbox・マンガでわかるLINE Clova開発・マンガでわかる衛星データ活用といった分野横断的なコンテンツを展開している。
・Amazon著者ページ
・Twitterアカウント

  わかばちゃんが登場する書籍いろいろ   

 
 
f:id:itstaffing:20210113102514j:plain  f:id:itstaffing:20210113102516j:plain
 
f:id:itstaffing:20210113102519j:plain  f:id:itstaffing:20210113102521j:plain


わかばちゃんと学ぶシリーズ(Amazonページ)