因果推論の考え方

この記事は Monoxer Advent Calendar 2024 23日目の記事です。

はじめに

モノグサでData Scientistをやっている力徳と申します。

モノグサには、今年の8月からjoinしていますが、その前の会社では、R&D Engineer、Data Engineer、Data Scientist、ML Engineer、NLP Engineerとしてたくさん(笑)の会社で働いておりました。

簡単にいうと、データ分析に関わる雑用係をいろんな会社でやっていました(笑)

そのときに、biz系組織の方々からよく要望としてもらっていたのが

  • KPIを押し上げる”ドライバー”を見つけたい!
  • データから"ビジネスインサイト"を抽出したい!
  • 施策の効果を検証したい!

などです。

これはだいたいどこの会社でも言われてました。これらの問題はKPIのような目的変数と因果関係がある"変数"を見つける、もしくは、その因果関係を推定する問題として捉えることができ、因果推論と呼ばれるデータサイエンスのテクニックを適用することができます。

今回は、この因果推論についてうす〜く解説したいと思います。

相関と因果の違い

まず、相関と因果は異なるという話を聞いたことがある方は多いのではないでしょうか? 下の図はその説明によく利用される 国別の「1人当たり年間チョコレート消費量と人口10万人当たりのノーベル賞受賞者」の関係性を示した散布図です。

縦軸が人口10万人当たりのノーベル賞受賞者、横軸が1人当たり年間チョコレート消費量を表しています。この散布図からチョコレートの消費量とノーベル賞受賞者数には相関があり、さらにそれはチョコレートの成分が脳に良い影響を及ぼしているからだという仮説を 参考情報[1]の不定期メモでは主張していたようです。

しかし、[2]の解説記事でも紹介されているように、チョコレートをたくさん食べるとその効果として頭がよくなり国のノーベル賞受賞者数が増加する、つまり、チョコレートの消費量とノーベル賞受賞者数には原因と結果の関係、因果関係があるというのは考えづらく、それぞれの量に共通する影響因子(交絡因子)がある、つまり疑似相関の可能性が高そうだと考えられます。

直感的にはそれっぽいですが、この相関と因果の違いをきちんと判別するフレームワークはあるでしょうか? このフレームワークが因果推論と呼ばれる統計学の1分野です。相関や相関係数なども統計学で曖昧性なく定義されているように、因果効果も統計学の言葉で定義することができます。

因果効果の定義

ルービンによる因果モデルにおいて、ある対象者 $i$に対する(潜在的)結果変数 $y_i$へのある処置$z$の因果効果は、それぞれの場合の結果変数の差として表現されます。

$$ y_{i, z=1} - y_{i, z=0} $$

ここで、$z=1$は因果の原因となるある処置を施した場合を表し、$z=0$は処置を施さなかった場合を表します。

処置を施されたグループは処置群(Treatment Group)、施されなかったグループは対照群(Control Group)と呼ばれます。ここで注意するのは、必ずしもこの比較が同一対象に対して全く同じ条件でできないということです。 例えば、ガンの治療薬のある患者への効果の因果関係を確かめるために、同じ患者$i$に治療薬を投薬した場合と、しなかった場合を比較することはできません。

ある勉強方法と学力向上の因果関係を推定するために、同じ学生$i$に対して、その勉強法を試した場合と試さなかった場合を同時に比較することはできません。

このようなどちらか片方の処置が実現され、他方の処置が実現されない場合には上の因果効果はどのように評価したらよいでしょうか?

答えは,1個人、1対象の因果効果を測定するのではなく、公平に選別された多人数からなる処置群と対照群での平均をもって因果効果の推定値とすることです。

$$ E(y_{z=1}) - E(y_{z=0}) $$

ここで、$E$は処置群、対照群それぞれの結果変数の平均(期待値)を表します。

これは、50%/50%で完全にランダムに分割されたユーザーに対するAB testであり、Webサービスなどの施策の効果検証によく使われている手法です。

AB testによる効果の検証が施策の結果への因果を推論と同等であることがわかると思います。

では、AB test、ランダムに処置群と対照群に割り当てて比較することが難しい場合、もしくは、完全にはランダムに割り当てていることが保証できない場合はどうすればよいでしょうか?

医学、教育の分野では倫理的な理由などから完全にランダムなAB testを実施することが難しい場合が多々あります。 また、AB testとして完全に計画されてない設定で得られた観察データからある処置効果の因果効果を推定したい場合があります。

この場合に使用できるテクニックの1つが傾向スコアとIPW推定量です。

傾向スコア、IPW推定量

傾向スコア、IPW(Inverse Probability Weighting)推定量による因果の推論は、大雑把にいうと"仮想的"なAB testを実施することと言えます。

そもそも、因果効果とは

  1. ある同一対象に対して処置を施す場合と施さない場合でその結果の差として因果を定義できます。
  2. それができない場合、完全にランダムに分けられた処置群、対照群の平均の差として因果効果を推論します。

2.のAB testでは対象を完全にランダムに分けることで、それぞれの対象のグループは特性的に偏っているとは考えらないと状態になっています。十分なサイズがあれば、処置群と対照群には"同じ"ような特性を持った対象がいて、その”同じ”ような特性を持ったペア同士の結果の差として効果を推定していたわけです。

2.のAB test, 完全にランダムに処置群と対照群に分けられない場合は、同じように”似た”対象を特定し、その”似ている”者同士で結果の差を推定、因果推論を行います。対象が処置群に割り当てられる確率を”傾向スコア”といい、この傾向スコアを使って、公平に結果の差の平均を推定する方法が IPW推定による因果推論です。

傾向スコア

対象$i$の共変量を $\boldsymbol{x}_i=(x_1, x_2,\cdots, x_k)$とし, $z_i$を処置の割り付け変数, $y_i$を結果変数とします。共変量というのは対象$i$の特徴、特性を示すもので、機械学習の文脈では特徴量、素性、説明変数と呼ばれるものです。例えば、あるサービスのユーザーの年齢、性別などのデモグラフィック情報や利用履歴情報だったりします。

AB testの様に、ランダムに分けられているわけではない処置群と対照群の$n$個のデータ $D$ がある場合

$$ D = \left\{ (y_i, z_i, \boldsymbol{x}_{i}) \mid i = 1, 2, \cdots, n \right\} $$

傾向スコア$e_i$は対象 $i$ の割り付け変数 $z=1$への割り付け確率 $$ e_i = P(z=1|\boldsymbol{x_i}) $$ として定義されます。データ$D$があるのでロジスティク回帰のような確率出力できる2値分類器により容易に推定することができます。

傾向スコアを使う理由の”お気持ち”はどのようなものでしょうか。今の場合処置群と対照群がランダムに割り当てられているわけではなく、単純に各群の結果変数の平均の差を取った場合”偏り”がある可能性があります。この”偏り”を補正する目的で傾向スコアが導入されました。

”強く無視できる割当条件”と呼ばれるある条件を処置群、対照群が満たしている場合、因果効果はIPW推定量と呼ばれる 傾向スコア $e_i$による重み付け推定値で推定できます。

因果効果のIPW推定量

IPW(Inverse Probability Weighting)推定量もRubinによる導入されました。前節からの説明と同じく、因果効果は以下のように"公平"な結果変数の差(の平均)として定義されます:

$$ y_{z=1} - y_{z=0} $$

AB testの場合はランダムに2群に分けてられるので”公平”性が満たされているので平均の差がそのまま因果効果を示していました。 偏りのありえる場合では傾向スコア$e_i$(の逆数)で重み付けすることでこの偏りを調整することができます。

$$ \begin{aligned} \widehat{E}(y_{z=1}) &= \frac{\sum_{i=1}^n \frac{z_i y_i}{e_i}}{\sum_{i=1}^n \frac{z_i}{e_i}}, \\ \widehat{E}(y_{z=0}) &= \frac{\sum_{i=1}^n \frac{(1-z_i) y_i}{1-e_i}}{\sum_{i=1}^n \frac{(1-z_i)}{1-e_i}} \end{aligned} $$

として傾向スコアで重み付け平均 $\widehat{E}(y_{z=1,0})$を使って形式的にはAB testの場合と同じ様に

$$ \widehat{E}(y_{z=1}) - \widehat{E}(y_{z=0}) $$

として、"強く無視できる割り付け"条件を満たしいる場合には因果効果を推定できます。

傾向スコアによるIPW推定量から因果フォレストへ

因果効果を推定する場合

  1. AB testを実行し、結果の平均の差で判定する
  2. AB testのようにランダムに2群を分けることが困難な場合は、傾向スコアによる重み付き平均の差で判定する

の2つを利用することができました。フレームワークとしては決まっており容易に因果推論を行うことができます。 ただし、2.の傾向スコアを用いて因果効果の推定をする場合は、傾向スコア推定の性能に大きく依存している問題があります。つまり、傾向スコアの推定値の信頼性が大きくない場合は因果効果の推定の信頼性も低くなる可能性があります。また、その影響度合いもはっきりとはわかってない状況です。また、最終的な推定値を得るために、独立な傾向スコアの推定と因果効果の推定の2段階の推定を行っていることも好ましい方法でないとも考えられます。

このような背景のもとで、最近、機械学習の手法をベースにした因果効果の推定手法が開発されました。それが決定木やランダムフォレストをベースにした因果ツリー、因果フォレストです。参考情報[5],[6],[7]

これら決定木ベースの因果推論手法は傾向スコアを用いる手法に比べて以下の優位性を持っています。

  1. 因果効果の推論用に通常の決定木、ランダムフォレストとほぼ同じ学習で推論器を構築できる。
  2. 得られた因果ツリー、因果フォレストは統計的に好ましい性質を持っていることが(ある条件のもとで)数学的に証明されている。
  3. この因果ツリー、因果フォレストを使って、全体としての処置の因果効果の推定はもちろん、各対象に対して影響を個別に推定できる。(異質処置効果の推定)

特に2.の統計的に好ましい性質(漸近正規性とそれに基づく信頼区間の推定)は実用上有益であり、傾向スコアが持っていなかった特性です。

また、統計的機械学習分野では1つのブレークスルーとして評価されていて、今後の学術上の発展と、実ビジネスへの応用も期待されています。

最後に

今回、因果推論の考え方を傾向スコアとそのIPW推定量といっしょにうす〜く解説しました。また、最新の因果推論の技術である因果ツリー、因果フォレストについてもこれまたうす〜く紹介しました。

この因果フォレストについては1、2年前から知っており、その漸近正規性の証明をぜひ理解したいと思っていました。今回のアドベントカレンダーの記事の執筆の話があって、真っ先に思い浮かんだテーマがこの因果フォレストでした。一ヶ月くらいあれば、証明の理解ができるだろうと踏んでいたのですが。。。。その証明が難解すぎて、このようなうす〜い紹介になってしまいました。

モノグサではデータドリブンな物事の進め方は普通になっており、AB testによる効果検証や学力の向上の効果の推定などにもデータ分析の手法、考え方が広く利用されています。

このような、データサイエンティストには夢のような環境であるモノグサ株式会社では 一緒に働く仲間を募集しています。私に因果フォレストの漸近正規性の証明を教えてくださる方お待ちしています!

参考情報

  1. Messerli FH. Chocolate consumption, cognitive function, and Nobel laureates. N Engl J Med. 2012 Oct 18;367(16):1562-4. doi: 10.1056/NEJMon1211064. Epub 2012 Oct 10. PMID: 23050509.
  2. チョコレートを食べると ノーベル賞がもらえるか?
  3. 統計的因果探索 (機械学習プロフェッショナルシリーズ)
  4. 調査観察データの統計科学: 因果推論・選択バイアス・データ融合 (シリーズ確率と情報の科学)
  5. Estimation and Inference of Heterogeneous Treatment Effects using Random Forests
  6. ランダムフォレストによる因果推論と最近の展開
  7. EconML

qiita.com

careers.monoxer.com

アプリストアのオーナーアカウントを共用アカウントに入れ替えた話

こんにちは!やまもと@テスト番長です。暑くて長い夏がようやく終わったと思ったら今年もあと残り3ヶ月、冷凍おせちをどこで買うか悩み始めた今日この頃です。

僕はモノグサでQM(Quality Management)チームのマネージャーを務めさせていただいております。今年、アプリストアのオーナーアカウントを共用アカウントに入れ替える作業をQAチームで行いましたのでその時の体験談をご紹介したいと思います。

経緯

MonoxerはAndroidとiOSの各プラットフォームに対応しており、それぞれのストアの管理者権限はCTOがオーナーとなっておりました。

組織が拡大するにつれ、ストアで発生する諸々の対応をCTOが逐一行うのは非効率的になっていました。

今後の事業展開を踏まえ、設定を変える手間がかさみそうなことから、今回アプリストアのオーナーアカウントの入れ替えを行うことになりました。

モノグサではサービスのリリース作業をQAチームで担当しており、ストア周りの設定に触れることも多いため、検討の結果QAマネージャーの私が移行作業の担当を引き受けることになりました。

iOSはオーナーを簡単に引き継ぐことのできる仕組みがあったのですが、Androidはオーナーを永続的なものと定義していて引き継がれることを前提としておらず、結果、アプリの譲渡と同じプロセスを踏む必要がありました。

(現在は会社組織の場合は個人アカウントで登録しないように注意書きがあるようです)

移行作業で色々と苦労がありましたので、同じような作業をする方がいらっしゃったら参考になるよう経験したことを書き残しておきたいと思います。

事前に調べたこと、決めたこと

アカウント移行で万が一サービス停止したら大問題ですので、移行の影響を綿密に調査して段取りを決めていかねばなりません。

情報収集の際に参考にしたサイトを以下に挙げます。(先人の方々に感謝です。)

・Zaim アプリの所有者権限を変更した際の苦労話 https://note.com/y_sumida/n/n91b0265c439a

・iOS/Androidアプリを譲渡またはアカウントを移行する方法 https://backapp.co.jp/blog/12099

調査が進むにつれ、色々と面倒な作業があることがわかりました。

iOSはAccount Holderを移譲すればOKで、割とシンプルでした。

問題はAndroidです。FAQを調べたところ、オーナー権限の譲渡は出来ないようでした。現行のデベロッパーアカウントは個人のアカウントで登録されており、他の用途でも使っているので共用に変えるわけにも行きません。

念の為サポートにも問い合わせしましたが、やはり譲渡手続きになるとのことで腹を括って準備を開始しました。

基本的に譲渡手続きであるため、移行できない情報(=譲渡の場合渡す必要のない情報)が幾つかあり、そのケアが必要かどうか整理する必要がありました。

また、デベロッパーIDが変わるため、登録してある箇所は全部入れ替える必要があります。

当該のヘルプを確認し、洗い出しを進めました(下記は2024年4月頃の情報です。最新情報はヘルプをご参照ください。)

  • 移行できる情報

    • アプリのユーザー、ダウンロードの統計情報、評価およびレビュー、コンテンツのレーティング、ストアの掲載情報
  • 移行できない情報や設定

    • 一括エクスポート レポート、売上レポート、収益レポート
      • この時点でストア課金が無く収益は見ていませんでした。
      • ここを考慮しないで済んだのはラッキーでした。
    • テスター グループなど
      • デベロッパー / テスターは最初から登録し直す必要があります。
  • 連携し直しになる統合サービス

    • Google アナリティクス
    • Google Developers Console プロジェクト
    • Firebase連携
    • FCM (Firebase Cloud Messaging)
      • Push通知で使っているため確認が必要でしたが、GCPのサポートに問い合わせた結果、対応不要でした。
  • Ad SDK の統合(AdMob を含む)
    • 未使用
  • managed Google Play ****限定公開アプリ
    • 該当せず
  • Google Play Developer API
    • 使用箇所があれば新しいデベロッパー IDに更新する必要あり

統合サービスへの影響を洗ううち、入れ替え時のラグが若干あるかもしれないことが気になり出し、影響度のヒアリングを行いました。アナリティクスなどが見れなくなって困ることがないか社内に確認を取りましたが、幸い数日程度のラグであれば問題ない状況でした。

切り替えに時間がかかるため、移行時期はビジネス上影響の出にくい時期を選ぶことにしました。

4月は新学期でMonoxerの導入作業が活発に行われているため、無用なトラブルを避けるために5月のGW以降に実施することになりました。

いざ移行開始

まずは移行先のアカウントを準備するところから始めます。

アカウントの管理方法、2段階認証の方法やネーミングも決めなくてはいけません。地味に手間なところです。

Android側の移行ではPlay Storeのデベロッパー名が一時的に変わるため、開始のタイミングでカスタマーサクセスチームを通じて全社に周知を行いました。

iOSアプリの移行手順

手順は以下の通りでした。

https://developer.apple.com/jp/support/account-holder-transfer/

  1. 受諾側アカウント(共用アカウント)の下準備
    1. メールアドレスを振り出してApple IDを取得
    2. 組織にデベロッパーとして招待する
    3. 本人確認を終わらせる
      1. ファクタ認証必須のため、電話番号も準備
  2. Account Holderの譲渡
    1. 現在のAccount Holder(CTO)が、受諾側アカウントを指定して譲渡手続きを行う
  3. Account Holderを受諾
    1. 受諾側アカウントに通知が来るので、契約を確認して同意する
      1. Account Holderになった旨の通知が届く
  4. サブスクリプションの更新設定
    1. 以前のAccount Holderが、サブスクリプションの自動更新をオフにする
    2. 新しいAccount Holderが、サブスクリプションに登録
      1. 有効期限の30日前から可能

概ね問題なく完了しました。

Androidアプリの移行手順

移行作業の開始を社内に宣言し、以下の手順を実施していきました。

  • 現在のデベロッパー名を「monoxer.com」→「monoxer.com(旧)」に変更
    • 移行先のアカウントを「monoxer.com」にするため、一時的にリネームしておく必要がありました。
    • 審査しているのか、反映まで2日ほどかかりました。
  • 移行先のアカウントでデベロッパー登録を行う(有料
    • デベロッパー登録には審査があり、完了までしばしリードタイムがあります。2023年ころに比べると、登録に必要な情報が増えているようでした。
    • D-U-N-S ナンバー
      • 入力する必要があり、情報を取得しました。
    • デベロッパーの連絡先登録
      • 確認の音声メッセージが会社番号に届く仕組みなのですが、早く終わらせたかったため 一時的に仮の番号で登録し、後日正式な番号に入れ替えました。
    • その他会社情報
      • 住所、ウェブサイト、メールアドレスなどが必要です。

ここまでで一旦移行先のアカウントが作成されました。アプリの配信や譲渡を受けられるような状態にするには、さらに追加で諸々の設定を進めていきます。

  • 本人確認
    • 会社設立証書、登記簿、税務署からの何らかの書類、納税証明書が必要
      • アップロードの容量制限が厳しく、今回使用した資料ではPDFをダウンサイズしておく必要がありました。
    • 確認書類に記載のある経営者が、身分証明書を送って登録する必要がありました。
  • Google Payments 販売アカウントを作成
    • Google Search Consoleで会社のウェブサイトを認証登録します
    • 案内に沿ってお支払いプロファイルを作成します
    • 銀行口座登録
      • 銀行口座を登録すると、確認用のデポジットが振り込まれます。
      • 経理に協力をお願いして確認を終えました。
  • 移行先のアカウントでテストアカウント、グループ等を作成し直す
    • 現行のアカウントと同じ内容で登録し直しました。
    • Googleからの連絡をMLにしてメンバーを招待すると良いようです。
  • GCPのサービスアカウントを招待する
    • 手元の環境ではユーザーとして登録すればOKでした。
  • レポートのダウンロード(念の為)
    • レポートは利用していないと聞いており対応不要なものでしたが、2024年分について落とせるものは落としておきました。

一連の設定が終わったらいよいよ移行リクエストです。

  • アプリの移行リクエストを送る
    • 元のアカウントと移行先アカウントの登録取引 ID を調べて元のアカウントからサポートに依頼します。
    • 新しいアカウントの準備が全部完了していないと、アプリの移行は行えません。

移行リクエストを送ってから2日程度でリクエストが処理されました。 移行後に一連の動作確認を行い、移行完了となりました。

  • 移行完了の確認
    • Play storeでアプリをダウンロード出来ること
    • push通知を送信出来ること
    • Firebaseで各種情報を閲覧可能なこと
    • アプリをアップデート出来ること(旧アカウントで作成したビルドを使いましたが、新アカウントでもビルドすべきでした。このあたりは後述します)

移行で躓いたポイント

移行中、幾つか躓いたポイントがあったので愚痴注意喚起しておきます。

想定外① Apple Developer Appの本人確認書類アップロード

iOS側の移行は概ね順調に進みましたが、一点困ったのが本人確認が運転免許証では通らず、パスポートを引っ張り出してやっと通ったことです。写りの問題なのかもしれませんが、もっと簡単に認証できると嬉しいところですね。

想定外② Google Play Consoleの移行先アカウントの本人確認

どうしても経営陣に作業依頼をせねばならず、使ったPDFが一発で通らなかったこともあり手間取りました。会社の書類と本人の確認と段階が分かれてるとありがたいなと思いました。

想定外③ Google Play Consoleのお支払いプロファイル

銀行のデポジット手続きは認識していたのですが、会社のウェブサイトをGoogle Search Consoleへ登録する必要があり、確認用のファイルをサイトにアップロードするのに少々時間を食いました。

そして作業漏れ発生

移行作業にはなんだかんだで1ヶ月程度かかり、一通り片付けてほっとしたところで作業漏れが判明しました。

Androidアプリの新しいバージョンのビルドをするところでCIが失敗、原因はGCPのサービスアカウントに対してmonoxerアプリのアクセス権限を付与出来ていなかったせいでした。

アプリ譲渡前の待ち時間に開発者アカウントを登録し直したため、その時点ではmonoxerアプリへの紐付けがまだ出来ておらず、作業漏れとなってしまいました。

GCPのサービスアカウントおよび開発メンバーのデベロッパーアカウントの紐付けを行い、すぐに復旧させることが出来ました。

最後に

こういう作業はふつう、インフラ系だったシニアなDevの方などが担当する作業で、QAがこの手の作業をすることは珍しいのではないでしょうか。慣れないところも多く、沢山の方の手助けをいただいてなんとか完了することが出来ました。 この記事が何かのご参考になれば幸いです。

モノグサQA募集中です

モノグサ株式会社では一緒に働く仲間を募集しています。 少しでも興味を持ってくださった方は、ぜひお話しましょう!

Monoxer Intern Report #19_数式正解判定アルゴリズムの実装

はじめに

モノグサ株式会社のソフトウェアエンジニアのインターンとして参加させていただいている堀毛晴輝です。 Intern Reportを書くのは2回目で、1回目の記事は こちらです。2022年夏のインターン終了後も、通年でインターンを継続させていただいています。

取り組んだこと

Monoxerでは学習者が数式を解答する形式の問題があります。

この形式では、学習者は数式を入力し解答します。しかし、この形式の問題では登録されている正解の数式と完全に一致しないと不正解となってしまっていました。 たとえば正解が  (x+2)(x+3) の問題では、  (x+3)(x+2) は不正解と判定されてしまっていました。

問題作成時に別解を登録することができるため、簡単な数式であれば考えられる別解を登録しておくことで解決できますが、複雑な数式になると考えうるすべての別解を登録するのはとても困難です。 「同値な数式を入力したのに、登録されている正解と一致しなかったために不正解となってしまう」というのは、 学習者にとってモチベーション低下につながってしまいます。また、Monoxerがユーザーの記憶を正しく判定できない、ということにもつながってしまうため、これを解消したいです。

以下、正解として登録した数式を「正解」、学習者が入力した数式を「解答」と呼びます。今回は正解に等号や不等号が含まれているものは対象としません。

数値的な一致判定

正解/解答の数式に対して、 x y などの変数についてそれぞれ同じ値を代入することを考えます。 こうすると、正解と解答の数式は( 0で割ったり平方根の中に負の数が入ったりなどしていないなら)それぞれ一つの実数値になります。 解答が正解と判定されるべき数式ならば、変数にどのような値を代入しても、この値は一致するはずです。

正解/解答の変数にランダムな変数を代入することを複数回行い、複数回全てでこの値が一致しているかで正誤判定をします。 先述した通り、 0 で割ったり平方根の中に負の数が入ったりしている時には評価ができないのですが、そのような時は正解と回答は共に評価できなくなるはずです。実際の実装ではランダム変数の代入を複数回行い、値が違ったりどちらか一方が評価できない時は不正解と判定し、最終的に評価した値が一致した回数が規定回数以上であれば正解と判定しています。 この判定は完全ではありませんが、実用上は十分な精度を持っていると考えています。

Monoxerでは数式をMathMLの形で扱っています(Monoxerで使われている MathML については こちら の記事が詳しいです)。

MathMLは木の構造をしています。この構造をうまく使い、式の値を求めます。例えば  3 ^ 2 + 1 をMathMLで表すと、

<math>
    <msup>
        <mn>3</mn>
        <mn>2</mn>
    </msup>
    <mo>+</mo>
    <mn>1</mn>
</math>

となります。この式を評価した値を求めることを考えます。 これは、下から順に各頂点の値を求めていくことを繰り返せば良いです。

 3 ^ 2 = 9 なので、

 9 + 1 = 10 なので、

一番上のmath要素の値が求めたい数式の値です! このように、「子要素から順に値を求めていく」ことを繰り返すことで、求めたい式の値を求めることができます。

競技プログラミング風に言うと、  \mathrm{dp[node]} := (\mathrm{node} を根とする部分木を評価した値) として、木DPをしているイメージです。

シンボル的な一致判定

「次の数式を因数分解せよ。  x ^ 2+5x+6 正解:  (x+2)(x+3)

この問題は、「因数分解せよ」という問題なので、解答として  (x+2)(x+3) (x+3)(x+2) は正解に、 x ^ 2+5x+6 は因数分解されていないので不正解となるべきです。 しかし、数値的な一致判定を用いると  x ^ 2+5x+6 は正解と判定されてしまいます。

そこで、「因数分解せよ」や「展開せよ」といった問題では、数値的な一致判定に加え、以下の条件:

  • 正解と解答に含まれる括弧の数が一致
  • 正解と解答に含まれる 各変数(mi要素)/各数字(mn要素)の個数が一致

を満たす時に正解と判定するようにしました。 これにより、

  •  (x+2)(x+3) には括弧が2組あるが、  x ^ 2+5x+6 には括弧が存在しないので不正解
  •  (x+2)(x+3) には括弧が2組あり、  x が2回、 2,3 が1回ずつある。  (x+3)(x+2) にも括弧が2組、$x$が2回、2,3が1回ずつあり、数値的な一致判定でも正解と判定されるため、正解

となり、「因数分解せよ」「展開せよ」といった問題でも想定通りに別解の判定を行うことができるようになりました。

採点方法の設定方法

具体的に、ユーザーは問題作成時に、採点方法を次の3つ:

  • 従来通り、正解と解答が完全に一致するかどうかで判定
  • 数値的な一致判定で判定
  • 数値的な一致判定とシンボル的な一致判定で判定

から選択できるようにしました。別解が考えられるような問題では数値的な一致判定を、「因数分解せよ」や「展開せよ」といった問題では数値的な一致判定とシンボル的な一致判定を選択することを想定しています。

工夫した箇所

特殊なケースの構文解析

数値評価を行うとき「下から順に値を求めていけばよい」と簡単に書きましたが、実はこれだけではうまくいきません。 たとえば  \sin ^ 2 xなどは、MathMLだと

<math>
    <msup>
        <mi>sin</mi>
        <mn>2</mn>
    </msup>
    <mi>x</mi>
</math>

と表現されます。これをそのまま数値評価しようとすると、 \sin ^ 2を計算しようとしてエラーになってしまいます。  \sin ^ 2 x = (\sin x) ^ 2 なので、数値評価を行う時に気をつけなければなりません。

評価不能な数式のvalidate

MonoxerではCSV形式でBookやMiniTestを作成できます。この時に採点方法を指定するのですが、 たとえば数値計算モードが設定されているのに正解の数式が数値評価できない数式だった時、それは作問ミスである可能性が高そうです。 そういった時に警告を出してあげることで、作問者がミスに気づくことができます。

数値計算モードが指定されたエントリの登録時に数値評価ができるかどうかをフロント側で判定し、できなかった場合は登録できないようにしました。

正解が順不同な時の正誤判定

たとえば 、

 60の約数を全て列挙してください。 正解:  1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60

という問題について、順序を入れ替えたものは全て正解としたいです。しかし、この場合順序の入れ替えは  12! = 479,001,600 個あり、これらを全て別解として登録することは非現実的です。 今までは答えが一意になるように問題文を工夫し、この問題では「昇順で解答してください」と問題文に入れることで対応していたのですが、順不同な時にもうまく動作するようにしました。

このような問題について、正解と解答の数式をそれぞれカンマで区切り、

  • 正解の個数と解答の個数が一致している
  • 正解と解答で、答えが一致するようなマッチングが存在する

時に正解となるようにしました。これにより、問題で「昇順で解答してください」のように解答方法を指定する必要がなくなりました。

感想

前回のインターンタスクではフロントエンドのみでしたが、今回のタスクでは

  • フロントエンド: 登録しようとしている数式がアプリで数値評価できるかどうかのチェック
  • バックエンド: 数式エントリの採点方法の情報を新たにデータベースに追加
  • アプリ(iOS/Android): 正誤判定ロジックの実装

と幅広く触ることができ、とても勉強になりました。

また、今回の実装をするにあたり、

  • 四則演算だけでなくルート、分数、冪乗などの記法を使ったMathMLを数値評価した時に、意図した計算順序で計算が行われているか
  • 評価不能と判定されるべき数式が、意図した理由で評価不能と判定されているか

などが正しく動作しているかを確認するためたくさんテストを書きました。 テストを書くことでバグがあることに気づいたということが何度もあったので、テストを書くことの重要さを身をもって体感することができました。 

数値評価の実装は木DPをしたり構文解析をしたりが必要で複雑な実装となってしまいましたが、 「どうしたら数式を意図した通りに解釈できるか」を考えるのが面白く、楽しみながらコードを書くことができました。

おわりに

モノグサ株式会社では一緒に働く仲間を募集しています。 少しでも興味を持ってくださった方は、ぜひお話しましょう! モノグサ株式会社の採用サイトです。モノグサは「記憶定着」をサポートする学習サービス「Monoxer」を運営する会社です。

careers.monoxer.com

Monoxer と組版: より良いコンテンツの表示を目指して

モノグサで Software Engineer として働いている杉江です。

Monoxer では、記憶対象が問題としてアプリ上で出題されます。その際に表示される問題文などのコンテンツが見やすく表示されることは重要です。今回は、一般的に知られているコンテンツ作成方式の種類を紹介し、また Monoxer での組版についてもお話しします。

コンテンツ作成方式の種類

電子書籍やブログ記事などのデジタルコンテンツは、デバイス上で文字や図表の集まりとして表示されます。文字や図表を画面上に配置する工程は「組版」と呼ばれます。

コンテンツ作成方式は大きく分けて「リフロー型」と「フィックス型」の 2 種類があります。組版の工程をどの段階で行うかが異なります。

リフロー型は、表示するデバイスの画面サイズや設定に合わせて、テキストやレイアウトが流動的に表示される方式です。この場合、コンテンツは文字列として保持されていて、閲覧するたびにデバイス側でレンダリングしてコンテンツを表示します。

フィックス型は、表示するデバイスの画面サイズや設定に関わらず、文字や図表などのレイアウトが固定される方式です。この場合、コンテンツは画像や PDF などの形で保持されていて、デバイス側では組版処理は行わずに単に表示だけを行います。

Monoxer と組版

従来の Monoxer における組版と、その問題点

これまでの Monoxer はリフロー型のみに対応しており、アプリ側でコンテンツのレイアウトを決めて表示していました。

なお、コンテンツには 1 枚の画像を「挿絵」として挿入でき、挿絵は常にコンテンツの最上部に、決められたサイズに収まるよう配置されます。図を使って表す必要がある要素が複数ある場合は、それらを 1 枚の画像に収めたうえで、「図の(ア)は○○であり〜」のように、各要素にラベルをつけて指し示す必要がありました。

例として連立方程式が含まれたコンテンツを考えてみます。連立方程式の部分を正確に表記するには、数式を挿絵にする必要があり、その際は以下のようにそれぞれの式に(ア)と(イ)というラベルをつけて指し示すことでコンテンツを作成できます。

数学のコンテンツ制作を検討していく中で、この仕様ではレイアウトの自由度が低く、表現上で限界が出るのではないかという懸念が生まれました。仕様に関する主な問題点は次のとおりです。

  • コンテンツに必要な図表を、1 枚の画像に収めることが難しい場合がある

    • 数学では図表を多用する単元もあります。このような単元で、問題を解くために必要な画像群を 1 枚に全て収めようとすると無理が出てくる場合があります。
  • 文と文の間に画像を挿入できず、学習体験として望ましくない状況になることがある

    • 画像は最上部に表示され、文と文の間に画像を挿入できません。先ほどの連立方程式の例では、(ア)や(イ)に対応する式を見るときに、コンテンツの最上部にある画像に再び注目する必要があるため、視線が不自然に上下してしまいます。もしも(ア)や(イ)が書かれている場所に式がそのまま書かれていれば、上から下へ流れにそって、より自然にコンテンツを読めるようになる可能性があります。

また、これらの問題は数学だけでなく、他の教科や分野でも起こりうる問題ではないかと考えました。

課題解決に向けた方針検討

これらの課題を解決するために、リフロー型の方式を維持してアプリ側を改修する方針と、フィックス型の方式にも対応する方針の 2 つを検討しました。

Monoxer には、今後もさまざまな教科や分野のコンテンツが搭載される可能性があります。そのため、レイアウトに関するコンテンツ作成者からの要望も多岐にわたると予想されます。現状の仕組みで対応できないコンテンツに直面するたびに、必要な条件を洗い出してアプリ側を改修し、リフロー型の方式のみで対応し続けることは工数的・技術的に困難であると考えました。

一方でフィックス型の方式では、コンテンツは後述するように画像であり、レイアウトに関する要望は画像側(コンテンツ側)で対処すればよくなります。アプリ側では、コンテンツである画像を適切に表示できれば十分です。レイアウトに関する個々の要望をコンテンツ作成者側に移譲できることが決め手となり、フィックス型の方式に対応する方針を採用しました。

フィックス型の導入

今回導入した方式では、コンテンツは画像として保持されており、コンテンツが縦に長い場合は縦にスクロールすることで全体を閲覧できます。

画像がそのままコンテンツになるため、コンテンツ作成者は文面を自由に作成できます。たとえば先ほどの連立方程式の問題であれば、「次の連立方程式を」から「答えは x = 3, y = 2 である。」まで、問題文全体が 1 枚の画像になるように画像を作成します。この方式であれば、文と文の間に数式を挿入できるため、上から下へ流れにそってコンテンツを読めるようになりました。

Monoxer がフィックス型のコンテンツを導入してよかった点は、主に以下が挙げられます。

  • 複雑なレイアウトにも柔軟に対応できる

    • コンテンツ全体が画像であるため、画像さえ用意すればどのようなレイアウトでも表現可能です。
    • もちろん、文と文の間に図表を入れたり、複数の図表を含めたりすることができます。
  • コンテンツのレイアウトに関する個々の要望を、アプリ側ではなくコンテンツ側で対処できる

    • 先に述べたように、リフロー型のみで対処するとアプリ開発がボトルネックになるうえ、個々の要望に対応しきれなくなることが予想されます。フィックス型に対応したことで、レイアウトの複雑さをコンテンツ側で対処できるようになったため、このような懸念を払拭できた点がよかったと考えています。
  • 組版に関する既存の資産を活用できる

    • 出版社やライターなどのコンテンツ作成者は、組版に関するツールやスキルをすでにお持ちであると考えています。また、組版結果を画像形式で出力することは一般的に行われています。今回導入した方式では、画像がそのままコンテンツになるため、Monoxer に搭載するコンテンツを作成するときに、組版に関するツールやスキルを活用しやすくなります。

今後の課題

今回の改修によって、まだすべてのコンテンツがカバーできたわけではありません。たとえば、縦書きのコンテンツが入力されたときは、縦方向のスクロールだけでは対応できないかもしれません。教科や分野に応じてより良い学習体験を実現できるように、今後もさまざまな方針を検討していきたいです。

まとめ

コンテンツの作成方式にはリフロー型とフィックス型があり、それぞれ異なる強みを持っています。 従来の Monoxer ではリフロー型にのみ対応しており、また画像は決まった位置にしか挿入できませんでした。フィックス型にも対応できるように改修したことで、文中に図表を挿入したり、複数の画像を挿入したりなど、より柔軟にコンテンツのレイアウトを決められるようになりました。

モノグサ株式会社では一緒に働く仲間を募集しています。
少しでも興味を持ってくださった方は、ぜひお話しましょう!
モノグサ株式会社の採用サイトです。モノグサは「記憶定着」をサポートする学習サービス「Monoxer」を運営する会社です。

careers.monoxer.com

JaSST `24 Tokyo参加レポート

こんにちは!モノグサでQAEをしている甲斐と申します。 2024年3月14日(木)、3月15日(金)に開催されたJaSST'24 Tokyoに弊社のQAEとTest Automation Engineerの6名で参加してきましたので、その感想をレポートします。

JaSSTとは

JaSSTはソフトウェアテスト技術振興協会(ASTER)が開催する、テスト技術力の向上と普及を目的としたソフトウェアテストシンポジウムです。2020年からはしばらくの間オンラインのみの開催でしたが、今年(2024年)からオンラインと現地でのハイブリッド開催となりました。 私は初日を現地で、2日目をオンラインで参加しました。現地の基調講演では会場の8割前後が埋まるくらい盛況でした。現地には現地の、オンラインにはオンラインの良さがあったので、ハイブリッド方式の開催がとてもよかったです。 現地で実際に講演者の声や空気を感じるのもよかったのですが、オンラインの特性を活用して例えばDiscordでわからない用語についてURLを貼ってくださる方がいたり、社内のSlackでその瞬間刺さった言葉の感想や各個人が課題に感じていて改善したいと思っていることを意見交換できたり、プロアクティブに講演を理解できる経験ができてよかったです。

社内Slackの様子

メイン会場の後ろにはスポンサーのブースがあり、あまりITのイメージがなかったChocozapさんがスポンサーとしてブースをされていてびっくりしました。私はアンケートに記入してロゴ入りグッズをいただきました、ありがとうございます!

タオルはけっこうな頻度で使わせていただいてます!

講演の感想

初めてJaSSTに参加したのですが、B3講演「QAエンジニアの〇〇UP!キャリア解剖で見える今どきの成長軸とは?」で言及されていた、「現場で得られるものは断片で、シンポジウムや勉強で断片を体系化する」という話がとても印象に残りました。 普段仕事の中であまり意識しておらず、自分のキャリアアップとはテストをうまくできることに終始していましたが、テストの作業はソフトウェアやサービスの先にいる顧客に満足のいく品質で届けるためという俯瞰的な視点が抜けていたなと感じています。

他にも社内ではこんな感想が出ました:

  • 両日オンライン参加だったが、社内slack実況もdiscordでのチャットも盛り上がっていたので楽しく過ごせた
  • 登壇者のトレンドとして、QAの存在価値(そもそも、なぜQAが必要なのか)をきちんと(どのような指標が満たされれば達成されるのか)定義しよう、という論調が多かった
  • 今回は全体的にQAは品質をリードする存在であると言う内容の話が多かった気がする
  • 勉強ではなく知識を得て使え、というお話がありましたがまずは知識を得る勉強が必要だと思いました。
  • プロセス改善を地道に進める話や品質の考え方のセッションが多くあり参考になった。

JaSST 2024 Tokyoの参加は、私たちモノグサのQAチームにとってとても有益な経験になりました。個人としてもチームとしても自分たちの仕事に対する視野が広がり、これから取り組んでいきたい課題に対する理解も深まったと感じています。次回も是非参加させていただきたいと思います!

JaSSTに参加された方は、各講演の録画が公開されてますのであらためて視聴してみてもいいかなと思います。JaSSTからの案内メールか、公式Discordの「総合案内」にURLがあります(URLは転載禁止のため案内だけにしております。)

Monoxer Intern Report #18_Webクライアントにおける数式入力手段の拡充

自己紹介

 2024 年春にモノグサで SWE としてインターンに参加しました、宮崎と申します。2024 年 3 月現在、慶應義塾大学理工学部の 1 年生です。

参加を決めた理由

 もともと春休みに大きな予定がなく、何かしらの春季インターンに参加することは考えていました。そんなとき、AtCoderJobs でモノグサの募集を見て、実際のサービスの開発に携われることに魅力を感じたこと、また AtCoder Beginner Contest や ICPC のスポンサーなどを通して会社について認知していたことから、応募を決めました。

取り組んだこと

 主に管理者用 Web クライアントでの Book 編集機能に関するタスクを担当しました。その中でもメインタスクとして、数式の入力手段の拡充に取り組みました。

メインタスクについて

 Monoxer では数学の問題を出題することができますが、そのためには自ずと数式を入力する機能が必要になります。Monoxer では MathML と簡易数式という 2 種類の数式入力手段をサポートしており、前者はマークアップ言語であり、後者は AsciiMath をベースとした記法となっています。

図1. MathML と簡易数式を記述する例

 図 1 からわかるように、MathML は文法が明確に定まっておりプログラム上では扱いやすい一方、タイプ量が多いことや人にとって読みづらいことからユーザにとって負担が大きいです。それに対して、簡易数式は直感的な記述ができるため、簡易数式を使える場面を増やすことが目標となっています。

 現状の Web クライアントにはとある問題があり、それは図 2 のように簡易数式で入力しても、一度保存して再度編集しようとすると MathML に変換されてしまうことです。

図2. 簡易数式が MathML に変換される例

 これは仕様であり、入力された数式のデータはすべて MathML に変換された上でデータベースに保存されていることに起因します。

 この問題を解消すべく、私は解答の入力欄に MathML と簡易数式を相互に変換するボタンを配置し、ユーザの操作により切り替えられるようにしました。

図3. MathML と簡易数式を切り替えるラジオボタン

実装について

 ここで、簡易数式 → MathML の変換は既にあるものを流用することができましたが、その逆である MathML → 簡易数式の変換は新たに実装する必要がありました。

 MathML はマークアップ言語のため木構造が成り立っており、深さ優先探索 (DFS) により容易に探索することができます。そこで私は「自らを根とする部分木に対する変換結果」を返す関数をつくり、再帰的に変換する実装としました。

図4. MathML を深さ優先探索により探索する例

 また、React を用いた UI の実装も担当しました。

インターンの感想

 今回参加したインターンシップではとても多くのことを学びました。その中でもいちばん大きなものが、チーム開発の流れです。たとえば製品に 1 つの変更を加えるだけでも、最初の要件定義や実装後のコードレビューなど、多くの段階を踏む必要があることを実感しました。さらに保守性を高めるため、読みやすいコードを書くことやテストを作成することなども初めての経験であり、重要な学びとなりました。

 コーディングの面では、TypeScript や React が完全に未経験の状態だったため、一から調べながら実装していましたが、そのような状態からこのような成果を出せたのは大きな成長であったと感じます。

 そして、働く環境はとても良かったです。ボードゲームの文化が端的な例ですが、それ以外にもあらゆる場面で伸び伸びと働ける雰囲気がありました。また、我々インターン生の考えも社内で真剣に扱ってもらえたことが嬉しく、印象に残っています。

 その反面大変だったこととしては、必要な知識を自分で手にいれる必要があったことが挙げられます。たとえば React は、公式のチュートリアルなども活用して習得していきました。もっとも、そのおかげでスキルをよりしっかりと身につけられたとも考えています。

 最後のまとめとなりますが、初めてのインターンであり製品の開発経験もなかった私がこのような成果を出せたことはとても貴重な経験であり、参加して良かったと感じています。私を支えてくださった社員の方々に深く感謝します。

Monoxer Intern Report #17_CSV Data Export機能の追加と改善

自己紹介

モノグサの春インターンに2ヶ月間参加した中尾です。春から立命館大学の学部3年生です。普段は技術書や技術記事を読んだりしています。今回は遠方からの参加ということで、最初と最後に1週間強ほどずつ出社し、他はリモートでの勤務となっていました。

参加を決めた理由

春のインターン生をAtCoderJobsで募集しているというポストをXでみた友人に教えてもらって存在を知りました。AtCoder自体は最近はやっていなかったのですが、半年ほど夢中で取り組んでいたので深い思い入れがありましたし、モノグサに関してもAtCoderをやっていた時に解説記事などでお世話になったけんちょんさんが入社したというポストを見たことがあったので、存在は知っていました。当時ちょうどインターンに参加したいと思って色々探していた私にはまさに渡りに船で、すぐに応募を決めました。モノグサのブログやインタビュー記事、ネットニュースなどを調べるにつれ、参加したいという思いは強まっていきました。そこで実際に面接を受けた結果、選考を通過することができ、参加することを決めました。

取り組んだこと

組織の管理者がユーザを管理するためのWeb管理ツールにある、CSV Data Export機能の追加や改善に取り組みました。 CSV Data Export機能とは、ユーザに関する情報をCSVファイル形式で出力できる機能です。ダウンロードしたCSVファイルをExcelに取り込んで、データを分析するために使用されます。現在は、一部の組織向けに限定で公開している機能です。 私が取り組んだ課題は主に3つあります。

記憶度の出力項目に所属クラス情報を追加する

課題背景

CSV Data Export機能に、ユーザの記憶度を出力できるものがあります。記憶度とは、学習するBookに対して、どのくらい記憶しているのかを表す指標です。組織の管理者は、情報が欲しいBookを選択して、組織に所属する各ユーザの選択されたBookに対する記憶度をCSVファイルとして出力することができます。 今まで、この機能に対して、各ユーザの「スクールID」「スクール名」「クラスID」「クラス名」などの所属クラスの情報が付属していませんでした。なので、所属クラスの情報を必要とする分析を行うためには、お客様自身で、出力したCSVファイルとユーザの所属クラスの情報を組み合わせる必要がありました。 また、「学習回数の出力」などの他の機能にはこれらのクラス情報が付属していたため、記憶度と他の機能の出力項目の違いをお客様に説明する必要もありました。 よって、この課題を解決することで、お客様の手間も省け、ビジネスチームがお客様に出力項目の違いを説明する必要もなくなります。

やったこと

フロントエンドの実装

「出力するCSVデータの項目」に所属クラス情報を追加しました。

記憶度の出力予約画面

バックエンドの実装

ユーザの「組織メンバーID」から所属クラス情報が取得できるMapを作成し、それを使って出力項目に追加していきました。

結果

実装箇所としてはあまり多くないのですが、ビジネスとしてはインパクトがあると考えられます。 記憶度の出力項目に所属クラスの情報が追加されたことで、お客様自身で所属クラスの情報を付与する手間がなくなりました。また、ビジネスチームも他の機能との出力項目の違いをお客様に説明する必要がなくなりました。

管理者週間アクティブ率を出力できるようにする

課題背景

管理者週間アクティブ率とは、1週間でWeb管理ツールを使用した管理者の割合を表すものです。塾現場では管理者を管理したいというニーズが高いため、この機能を追加することになりました。この機能は、Monoxerを導入した組織が、出力されたデータを基に管理者がきちんとWeb管理ツールを利用しているかを確認し、利用を促したりするために利用されます。

やったこと

フロントエンドの実装

「管理者アクティブ率の出力」というボタンを設置し、そこから出力予約画面に飛べるようにしました。

出力予約画面への遷移ボタン

出力予約画面では、集計する期間や集計対象を選択して、出力予約が行えるようにしました。Monoxerでは組織階層の下にスクール階層というものがあり、組織AにはスクールA, スクールB, スクールCがあるというようになっています。よって、集計対象としてスクールを選択することで、スクール単位での管理者のアクティブ率というのも出力できるようにしました。

出力予約画面

ちなみになぜ「出力」ではなく「出力予約」なのかというと、データの出力に時間がかかるからです。先ほど紹介した記憶度の出力だと、長い場合2時間半ほどかかるので、出力予約という形にして、CSVファイルが準備できたらダウンロードできるようにしています。

バックエンドの実装

こちらは2ステップあります。

  1. 出力予約リクエストの処理

    出力予約画面で選択されたパラメータを含むリクエストが飛んでくるので、それを処理します。任意の出力予約を処理するハンドラがあるので、そこに今回の機能の処理を追加する形になります。集計期間が意図された値か、集計対象が存在しているかなどをチェックして、OKであれば予約をDBに格納します。なぜDBに格納するかというと、CSVファイルの生成はバッチ処理で行う仕組みになっているためです。

  2. CSVファイルの生成

    こちらでは管理者のアクティブ率を計算するため必要なデータをDBから取得して、出力するデータを算出し、CSVファイルを生成します。 まず、管理者週間アクティブ率を計算するためには「組織の管理者一覧」と「対象期間の管理者のアクセスログ」が必要になるので、それらをDBから取得します。 次に、集計期間を1週間ごとに区切って、各期間でアクセスしていた管理者の数をカウントし、アクティブ率を計算します。 このようにして、「集計開始日」「集計終了日」「アクティブ率」をCSVデータとして出力します。

    以上が組織単位での管理者アクティブ率の出力方法です。スクール単位だともう少し処理が複雑になりますが、流れとしては同じです。

結果

管理者週間アクティブ率が出力できるようになったことで、Monoxerの利用実態についてデータに基づいた分析や利用の促進が可能になり、お客様の「組織の管理者を管理したい」というニーズを満たすことができました。

追加の実装箇所

実は、記憶度の出力項目に所属クラスの情報を追加する課題が終わった後に、大手のお客様が記憶度を出力しようとするとエラーになってしまう問題が発生しました。調査の結果、私が実装した箇所に不備があったことが発覚し、それを修正することで問題は解決しました。レビューはしっかりと行っていたのですが、それでも不備が入り込んでしまったことの原因として、CSVファイルを生成する関数にテストが存在しないことがありました。これらの関数は複雑な処理をしているため、レビューだけでバグを無くすのは困難です。よって、今回の管理者週間アクティブ率を出力する関数にはテストを追加することにしました。結果として不備が2つ見つかり、そのうちの1つは実際にアクティブ率の計算結果に影響を及ぼす可能性のあるものでした。このように、早期に不備に対応することができ、他のCSVファイル生成関数にもテストを追加する足がかりを作ることができたのは、地味ではありますが今回のインターンの中でも重要な成果であると思っています。

出力項目に属性情報を追加できるようにする

課題背景

属性情報とは、「学年」「出席番号」「校舎」「選択科目」など、組織で個別に設定できるユーザに関する情報のことです。現状では、これらをCSVファイルの項目に追加することができません。よって、これらの情報を用いて分析を行いたい場合は、お客様自身でCSVファイルとアカウント情報を組み合わせる必要があります。このような作業は二度手間です。また、そもそもCSVファイルを分析ではなく表のように扱いたい小規模な組織のお客様にとっては、完全に手作業で属性情報を付与していくことによってミスをしてしまう危険性もあります。 よって、この課題を解決することで、お客様の苦しみを解決することができます。

やったこと

Design Docの執筆

機能の要件を満たす上で、どの方針にどんなメリット、デメリットがあるのかを記述し、どれをなぜ採用したのか。不安要素は何で、それは実際に問題となるのか。などを記述しました。 例えば、属性選択のコンポーネントのデザインで以下の3つが良い方針として浮かび上がりました。

  • 画面スクロール
    • メリット
      • ページに比べて操作が楽
    • デメリット
      • 既存の基盤コンポーネントがなく、新たに実装する必要がある
      • デザインを工夫しないと、スクロールできることがわかりづらい
      • Web管理ツールのターゲットによっては、必ずしもスクロールが使いやすいデザインであるとは限らない

スクロールでの選択画面

  • ページネーション
    • メリット
      • 既存の基盤コンポーネントが使用できる
      • 操作方法がわかりやすい
      • デザインとして見栄えが良い
    • デメリット
      • ページ遷移の操作がやや面倒
      • 表示件数を増やすと、縦に伸びて画面を占有してしまう
      • 属性の追加は必須項目でないのに、画面の占有率が高く、存在感が大きい

ページネーションでの選択画面

  • Modal&ページネーション
    • メリット
      • 既存の基盤コンポーネントが使用できる
      • 操作方法がわかりやすい
      • 表示件数を増やしても予約画面には影響がない
    • デメリット
      • ページ遷移の操作がやや面倒
      • デザインとして単純なページネーションに比べて、見栄えが悪い

Modal&ページネーションでの選択画面

このように、それぞれの方法のメリットデメリットを検討し、結果としてModal&ページネーションを採用しました。特に単純なページネーションではなくModalを採用した理由としては、「属性の追加は必須項目でないのに、画面の占有率が高く、存在感が大きい」がクリティカルでした。 最初私はスクロールを推していましたが、「Web管理ツールのターゲットによっては、必ずしもスクロールが良いデザインであるとは限らない」というのは考えられておらず、人によって良いデザインは異なるかもしれないというのは私にとって深い学びになりました。

他にも、出力項目に属性を追加する必要があるのですが、無制限に追加できるようにするとデザインが崩れてしまう恐れがあります。よって、以下の2つの案を検討しました。

  • 選択数に制限を持たせる
    • メリット
      • 何が追加されるのか明確にわかる
    • デメリット
      • 制限を持たせることによって、お客様が不便に思うケースが生じる可能性がある
  • 「選択された属性情報」などの表現で代替
    • メリット
      • 選択数に制限がない
    • デメリット
      • 何が追加されるのかが明確に表現できない

これに関してはビジネス側とも連携をとって、どのくらいの選択数が必要かを確認しました。すると、5つあれば十分との回答がありました。また、選択数に制限を持たせることでCSVファイルのサイズも抑えられるので、この方針に決定しました。このように、ビジネスの都合だったり、付随する問題についても考慮して設計を行うのは面白いなと思いました。

フロントエンドの実装

予約画面で追加する属性を選択できるようにしました。 選択し直す際に選択している項目が上にくるようにしたり、検索ボタンをなくして入力するだけで動的に検索結果が表示されるようにするなど、お客様が使いやすいよう工夫しました。

出力予約画面での追加する属性の選択ボタン

追加する属性の選択Modal

バックエンドの実装

管理者週間アクティブ率の出力と流れとしては同じです。

  1. 出力予約リクエストの処理

    選択された属性の数が制限を超えていないか、選択された属性が本当にその組織に存在するのかなどをチェックします。OKであれば予約をDBに格納します。

  2. CSVファイルの生成

    選択された属性の情報、組織のメンバーに対する属性の値をDBから取得します。ユーザの「組織のメンバーID」から属性の値の配列が取得できるMapを作成し、それを使って出力項目に追加していきました。

結果

属性情報を出力項目に追加できるようになり、お客様が手作業で属性情報をCSVに付与する必要がなくなりました。これにより、簡単に属性情報を必要とする分析が可能になり、お客様の苦しみが解決されました!

インターンの感想

この2ヶ月間で、本当に様々なことを学ばせていただきました。開発の観点では、Design Docを書いて、実装をして、テストを書いて、レビューをいただいて修正するという開発の一連のプロセスを経験することができました。その経験を通して、レビューやテストの大切さ、設計をする上では様々なことを考慮しなければならないこと、どのようなコードを書くべきかなどを学ぶことができました。経験の観点では、実際に2ヶ月間チームの一員として働くことで、会社の雰囲気や仕事上のコミュニケーション、プロダクトに対してどのように人が関わっているかなど、仕事に対する具体的なイメージを持つことができました。また、開発をしていると設計や実装において意見がぶつかることがありますが、その時にきちんと自分の意見を伝え、相手の意図を汲み取って尊重し、お互いが気持ちよく合意を取れるように議論をするというのがとても大切だと思いましたし、そのような環境が構築できているモノグサは良い会社だなと思います。 このインターンを通して、エンジニアとしてとても成長できたと感じています。この経験を活かして、今後も成長できるように精進していきます。このような機会を提供してくださったモノグサの皆様に深く感謝を申し上げます。2ヶ月間ありがとうございました!