「PySpark UDFを使用して合成テーブルの列間にOne-To-Oneの関係を作成する方法」
「PySpark UDFを使って合成テーブルの列間にOne-To-Oneの関係を作成する方法」
テストテーブルで関連する列を生成するために、いくつかの簡単な方程式を利用しましょう。
最近、Databricks Labs Data Generatorを使って、完全に合成されたデータセットを作成しています。これにより、異なる店舗、従業員、および顧客に関連する販売データを構築することができます。したがって、人工的に生成している列間の関係(従業員と顧客を特定の店舗に割り当てるなど)を作成したいと思いました。
PySpark UDFと少しの論理を使うことで、多対一の関係に従う関連する列を生成することができます。さらに、マッピングにいくらかの変動性を与えるためのマジックを使うこともできます。例えば、顧客は通常は地元の店舗から購入しますが、時には異なる店舗からも購入するといった具合です。
Databricks Labs Data Generatorを使用して基本的なDataFrameを生成する
注:必要な場合は、このセクションをスキップできます!
まずは、最初のランダム生成列を持つDataFrameを作成する必要があります。この場合、論理的には「店舗ごとに多くの従業員」と「店舗での買い物を繰り返す多くの顧客」がいると考えられます。
Star Schemaデータモデルを考えると、Sales Factテーブルから始めます。これは販売ID、店舗ID、従業員ID、顧客IDのキーバリュー、販売金額、および購入のためのいくつかの日時データを含む取引テーブルです。その後、店舗、従業員、および顧客に関する詳細を次の段階で埋めていきます。
小さいものから始めましょう – 1000の販売を含むテーブルで十分です。今度は、これらの販売を店舗、従業員、および顧客の間でどのように分割するかを決める必要があります。以下のように提案します:
- # 店舗数 = 20
- # 従業員数 = 100
- # 顧客数 = 700
また、販売は先月の間に記録されると仮定します:
- 最初の販売日 = 2023年11月01日
- 最後の販売日 = 2023年11月30日
販売IDは一意の列である必要があるため、このためにID列を生成します。さらに、1000の販売を20の店舗に分配する必要があります。単純化のために、これはランダムとします。
Databricks Lab Generatorを使用して、次のコードを実行できます:
次に、販売が行われた日時とその金額を記録するためのコードを追加しましょう。シンプルにするために、販売のタイムスタンプを最も近い時間まで丸めます。
販売金額を計算するために、withColumn式の”expr”パラメータを使用して、いくつかのルール/範囲でランダムな数値を生成できます。
この場合、式は非常にシンプルです:ランダムな数値(0から1の間)に0.1を加え(0でない販売値を保証)、350を乗算します。
DataFrameの基本的な形状ができたので、すべてをまとめます:
データの値の分布を見るために、クイックデータプロファイルを作成できます:
StoreIdの分布は20の店舗間で比較的均一であり、欠損値もなく、中央付近を平均値としています。タイムスタンプと金額の値も同様です。
従属多対一の列を追加する
それでは、Employee Idの列をDataFrameに追加しましょう。これでDatabricks Lab Data Generatorの作業は完了しましたので、PySparkの操作を使用してDataFrameに列を追加します。
コードから離れて、この問題を以下の文としてモデル化したいと思います:
- 20の店舗があります。
- それぞれの店舗には1人以上の従業員がいます。
- それぞれの従業員は1つの店舗で働いています。
まず、従業員を店舗ごとに分ける必要があります。次のPythonの関数を使用することができます:
従業員の運用を店舗ごとに分配したので、IDの割り当てを始めましょう!
employeesPerStoreリストは、店舗ごとの従業員IDが重複しないようにします。次の式を使用して、テーブルのセールに従業員IDをランダムに割り当てることができます:
この関数は現在、単一の値に対してのみ機能するため、それをPySpark DataFrameが機能的かつ迅速に処理できる何かに入れる必要があります。
PySpark UDFをwithColumnメソッドに渡すことができるため、この論理を関数に再フォーマットし、UDFに設定しましょう:
次に、この新しい列としてDataFrameで呼び出します:
私たちは、Databricksの可視化ツールを使用して、店舗ごとの従業員IDのdistinctカウントを表示することで、これが正しいかどうかをすばやくテストすることができます。これは私の好みですが、必要に応じてグループ化ロジックや他のプロットモジュールを使用することもできます。
重要な注意:この論理では、結果から従業員が抜けてしまう可能性があります。つまり、従業員が0のセールを行い、結果的にDataFrameに含まれないことがあります。次のセクションでは、すべての顧客がセールを記録することができるようにする方法を見ていきます。
顧客の列を追加する
顧客の列は少し異なります…使用状況では、顧客が1つの店舗で複数回買い物をすることが一般的であると示唆されていますが、完全に別の店舗に行くことも可能です。これをどのようにモデル化するか?
従業員の列の作業が完了したので、get_employees関数とUDFロジックを以下のように顧客にも繰り返すことができます:
ここでもいくつかの顧客が見落とされている可能性があります。これを修正するためのいくつかのアプローチを以下に示します:
- DataFrameにすべての顧客が含まれるまで、whileループで再計算します(非効率的で高コストで、無期限に実行される可能性があります)
- whileループでランダムに顧客IDを更新し、DataFrameにすべての顧客が含まれるようにします(同じ店舗を上書きするロジックが必要で、無期限に実行される可能性もあります)
- セールステーブル内のレコードが1つ以上あるすべての顧客IDのリストを返し、欠落しているIDが追加されるまでランダムに上書きします(同じ店舗の顧客を上書きするためのロジックが必要で、whileループのロジックも必要です)
- 逆のプロセスを行い、従業員から開始します。これにより、各従業員がランダムに行に割り当てられるようになります。その後、マッピングを使用してストアIDを適用できます。
おそらく最後のオプションが最も計算コストが低いことがわかるはずです-必要なすべてのコードがあるため、少しフォーマットを変更する必要があります。
私たちの新しいスクリプトは以下のようになります:
顧客へのランダム性の追加
必要なのは少しのランダム性ですが、それを定義する必要があります。この例では、各顧客が通常の店舗(「地元」の店舗)で買い物をする確率が90%であるとします。結果セットにすべての顧客を必要としない場合は、顧客_udfを次のように調整し、df2を使用できます:
この論理では、random.choices関数を使用して重み付きリストを供給し、単一の値を返します。
重み付けリストを計算するために、顧客の「地元」店舗の重みが90%で、残りの10%を他の店舗に割り当てる必要があるため、この場合は19の店舗があります。したがって、各他の店舗が選択される確率は10/19 = 0.526%になります。これらのパーセンテージで配列を作成することができます。それは以下のようなものになります:[0.526,0.526,0.526,…,90,0.526,…0.526]
random.choicesにこれを渡し、対応する重み付きリストからランダムにストアIDを選択し、これをcustomer_id変数の入力として使用します。
注意:random.choicesの出力はリストで返されます(k個の結果を要求できるため)、ストアIDを整数値として取得するためにリストの0番目の要素にアクセスしてください。
これを顧客を含むDataFrameと組み合わせる必要がある場合、プロセスをわずかに逆にすることができます。重みのロジックはまだ有効なので、これをプラグインしてランダムにストアを選択し、その結果として返すことができます。
結論
これでできました!列間の厳密なマッピングと緩いマッピングを持つ合成されたDataFrameです。これで、より詳細な情報を含む関連テーブル(ストア名、住所、従業員名、役職などの寸法テーブル)を作成するための次のステップに進むことができます。これは、Databricks Labs Data Generatorや他のツール/プロセスを使用して行うこともできます。
Databricks Labs Data GeneratorのGitHubリポジトリには、ドキュメントとともに素晴らしい例がいくつかありますので、詳しく知るためにぜひご覧ください。
私のすべてのコードは、次のGitHubリポジトリからアクセスできます。
このデモについてのご意見、コメント、代替案がありましたら、コメントでご連絡ください。ありがとうございました!
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles