Pythonを使用して強力なOLAPシステムとデータウェアハウスを設計および構築する方法を学びます。データモデリングからETL、Pandas、Dask、DuckDBなどの適切なツールの選択まで、すべてを網羅します。
Pythonデータウェアハウジング:OLAPシステム設計の包括的ガイド
今日のデータ駆動型世界において、膨大な量の情報を迅速に分析できる能力は、競争上の優位性にとどまらず、必要不可欠なものです。世界中の企業が、市場のトレンドを理解し、業務を最適化し、戦略的な意思決定を行うために、堅牢な分析に依存しています。この分析能力の中核をなすのが、データウェアハウス(DWH)とオンライン分析処理(OLAP)システムという2つの基本的な概念です。
従来、これらのシステムを構築するには、特殊で、多くの場合、独自仕様で高価なソフトウェアが必要でした。しかし、オープンソース技術の台頭により、データエンジニアリングは民主化されました。この流れを牽引しているのがPythonです。Pythonは、汎用性が高く強力な言語であり、エンドツーエンドのデータソリューションを構築するための優れた選択肢となる豊富なエコシステムを備えています。このガイドでは、グローバルなデータエンジニア、アーキテクト、開発者を対象に、Pythonスタックを使用したデータウェアハウジングおよびOLAPシステムの設計と実装について包括的に解説します。
パート1:ビジネスインテリジェンスの基礎 - DWHとOLAP
Pythonコードに入る前に、アーキテクチャの原則を理解することが重要です。よくある間違いは、運用データベースで直接分析を試みることです。これは、パフォーマンスの低下や不正確な洞察につながる可能性があります。これは、データウェアハウスとOLAPが解決するために設計された問題です。
データウェアハウス(DWH)とは?
データウェアハウスは、1つまたは複数の異なるソースから統合されたデータを保存する集中リポジトリです。その主な目的は、ビジネスインテリジェンス(BI)活動、特に分析とレポートをサポートすることです。組織の過去のデータに関する唯一の情報源として考えてください。
これは、日々のアプリケーション(例えば、eコマースのチェックアウトシステムや銀行の取引台帳)を動かすオンライン取引処理(OLTP)データベースとは対照的です。以下に簡単な比較を示します。
- ワークロード:OLTPシステムは、多数の小さく高速なトランザクション(読み取り、挿入、更新)を処理します。DWHは、数百万件のレコードをスキャンする、より少なく複雑で実行時間の長いクエリ(読み取り重視)に最適化されています。
- データ構造:OLTPデータベースは、データの整合性を確保し、冗長性を回避するために高度に正規化されています。DWHは、分析クエリを簡素化し高速化するために、しばしば非正規化されます。
- 目的:OLTPはビジネスを運営するためのものです。DWHはビジネスを分析するためのものです。
適切に設計されたDWHは、パイオニアであるビル・インモンに帰属されることが多い、4つの重要な特性によって特徴付けられます。
- 主題指向:データは、アプリケーションプロセスではなく、「顧客」、「製品」、「販売」などのビジネスの主要な主題を中心に編成されます。
- 統合:データはさまざまなソースから収集され、一貫した形式に統合されます。たとえば、「USA」、「United States」、「U.S.」はすべて、単一の「United States」エントリに標準化される場合があります。
- 時間変動:ウェアハウス内のデータは、長期的な時間軸(たとえば、5〜10年)にわたる情報を表し、過去の分析と傾向の特定を可能にします。
- 不揮発性:データがウェアハウスにロードされると、更新または削除されることはほとんどありません。それは過去のイベントの永続的な記録になります。
OLAP(オンライン分析処理)とは?
DWHが過去のデータのライブラリである場合、OLAPはそれを探索できる強力な検索エンジンおよび分析ツールです。OLAPは、OLAPキューブと呼ばれる多次元ビューに要約された情報をユーザーが迅速に分析できるようにするソフトウェアテクノロジーのカテゴリです。
OLAPキューブはOLAPの概念的な中心です。必ずしも物理的なデータ構造ではありませんが、データをモデル化し視覚化する方法です。キューブは以下で構成されます。
- メジャー:これらは、「収益」、「販売数量」、「利益」など、分析する定量的で数値的なデータポイントです。
- ディメンション:これらは、メジャーを記述し、コンテキストを提供するカテゴリ属性です。一般的なディメンションには、「時間」(年、四半期、月)、「地域」(国、地域、都市)、「製品」(カテゴリ、ブランド、SKU)が含まれます。
売上データのキューブを想像してみてください。さまざまなディメンションで総収益(メジャー)を見ることができます。OLAPを使用すると、このキューブに対して信じられないほどの速度で強力な操作を実行できます。
- スライス:1つのディメンションの単一の値を選択して、キューブの次元を削減します。例:「2023年第4四半期」のみの売上データを表示します。
- ダイス:複数のディメンションの値の範囲を指定して、サブキューブを選択します。例:「エレクトロニクス」および「アパレル」(製品ディメンション)の「ヨーロッパ」および「アジア」(地域ディメンション)の売上を表示します。
- ドリルダウン/ドリルアップ:ディメンション内の詳細レベルをナビゲートします。ドリルダウンは、高レベルの要約から低レベルの詳細に移動します(たとえば、「年」から「四半期」から「月」)。ドリルアップ(またはロールアップ)はその逆です。
- ピボット:キューブの軸を回転させて、データの新しいビューを取得します。例:「製品」軸と「地域」軸を交換して、どの地域がどの製品を購入しているかを確認します。
OLAPシステムの種類
OLAPシステムには、主に3つのアーキテクチャモデルがあります。
- MOLAP(多次元OLAP):これは「古典的な」キューブモデルです。データはDWHから抽出され、独自の多次元データベースに事前に集計されます。長所:すべての回答が事前に計算されているため、クエリのパフォーマンスが非常に高速です。短所:事前に集計されたセルの数が膨大になる可能性があるため、「データの爆発」につながる可能性があり、予期していなかった質問をする必要がある場合は、柔軟性が低下する可能性があります。
- ROLAP(リレーショナルOLAP):このモデルは、リレーショナルデータベース(通常はDWH自体)にデータを保持し、高度なメタデータレイヤーを使用して、OLAPクエリを標準SQLに変換します。長所:最新のリレーショナルデータベースの能力を活用するため、非常にスケーラブルであり、より詳細なリアルタイムデータをクエリできます。短所:集計がオンザフライで実行されるため、クエリのパフォーマンスはMOLAPよりも遅くなる可能性があります。
- HOLAP(ハイブリッドOLAP):このアプローチは、両方の長所を組み合わせようとしています。高速化のためにMOLAPスタイルのキューブに高レベルの集計データを保存し、詳細な分析のためにROLAPリレーショナルデータベースに詳細なデータを保持します。
Pythonで構築された最新のデータスタックの場合、境界線は曖昧になっています。信じられないほど高速なカラム型データベースの台頭により、ROLAPモデルが主流になり、非常に効果的になっています。多くの場合、剛性なしに従来のMOLAPシステムに匹敵するパフォーマンスを提供します。
パート2:データウェアハウジングのためのPythonエコシステム
従来、エンタープライズBIプラットフォームが独占していたタスクにPythonを選択するのはなぜですか?その答えは、その柔軟性、強力なエコシステム、およびデータライフサイクル全体を統合する能力にあります。
なぜPythonなのか?
- 統合言語:データ抽出(ETL)、変換、ロード、オーケストレーション、分析、機械学習、API開発にPythonを使用できます。これにより、複雑さが軽減され、異なる言語やツール間のコンテキスト切り替えの必要性がなくなります。
- 広大なライブラリエコシステム:Pythonには、データ操作(Pandas、Dask)からデータベースインタラクション(SQLAlchemy)やワークフロー管理(Airflow、Prefect)まで、プロセスのすべてのステップに対応する成熟した実績のあるライブラリがあります。
- ベンダーニュートラル:Pythonはオープンソースであり、すべてに接続します。データがPostgreSQLデータベース、Snowflakeウェアハウス、S3データレイク、またはGoogleシートにあるかどうかにかかわらず、それにアクセスするためのPythonライブラリがあります。
- スケーラビリティ:Pythonソリューションは、ラップトップで実行される単純なスクリプトから、DaskやSpark(PySpark経由)などのツールを使用してクラウドクラスター上のペタバイトのデータを処理する分散システムまで拡張できます。
データウェアハウススタックのコアPythonライブラリ
一般的なPythonベースのデータウェアハウジングソリューションは、単一の製品ではなく、強力なライブラリのキュレーションされたコレクションです。以下にエッセンシャルを示します。
ETL/ELT(抽出、変換、ロード)の場合
- Pandas:Pythonでのインメモリデータ操作のデファクトスタンダード。小規模から中規模のデータセット(数ギガバイトまで)の処理に最適です。そのDataFrameオブジェクトは、データのクリーニング、変換、分析に直感的で強力です。
- Dask:Python分析を拡張する並列コンピューティングライブラリ。Daskは、Pandas APIを模倣する並列DataFrameオブジェクトを提供しますが、チャンクに分割し、複数のコアまたはマシンで並行して処理することにより、メモリよりも大きいデータセットで動作できます。
- SQLAlchemy:Python向けの最高のSQLツールキットおよびオブジェクトリレーショナルマッパー(ORM)。SQLiteからBigQueryやRedshiftなどのエンタープライズグレードのウェアハウスまで、事実上すべてのSQLデータベースに接続するための一貫した高レベルAPIを提供します。
- ワークフローオーケストレーター(Airflow、Prefect、Dagster):データウェアハウスは、単一のスクリプトで構築されていません。これは、一連の依存タスク(Aからの抽出、Bの変換、Cへのロード、Dの確認)です。オーケストレーターを使用すると、これらのワークフローを有向非巡回グラフ(DAG)として定義し、堅牢性でスケジュール、監視、再試行できます。
データストレージと処理の場合
- クラウドDWHコネクタ:
snowflake-connector-python、google-cloud-bigquery、psycopg2(RedshiftおよびPostgreSQLの場合)などのライブラリを使用すると、主要なクラウドデータウェアハウスとのシームレスなインタラクションが可能になります。 - PyArrow:カラム型データ形式を扱うための重要なライブラリ。標準化されたインメモリ形式を提供し、システム間の高速データ転送を可能にします。Parquetなどの形式との効率的なインタラクションの背後にあるエンジンです。
- 最新のレイクハウスライブラリ:高度なセットアップの場合、
deltalake、py-icebergなどのライブラリ、およびSparkユーザーの場合は、これらの形式に対するPySparkのネイティブサポートにより、Pythonはウェアハウスの基盤となる信頼性の高いトランザクションデータレイクを構築できます。
パート3:Pythonを使用したOLAPシステムの設計
次に、理論から実践に移りましょう。分析システムを設計するためのステップバイステップガイドを次に示します。
ステップ1:分析のためのデータモデリング
優れたOLAPシステムの基礎は、そのデータモデルです。目標は、高速で直感的なクエリのためにデータを構造化することです。最も一般的で効果的なモデルは、スター型スキーマとそのバリアントであるスノーフレーク型スキーマです。
スター型スキーマとスノーフレーク型スキーマ
スター型スキーマは、データウェアハウスで最も広く使用されている構造です。これは以下で構成されます。
- 中央のファクトテーブル:メジャー(分析する数値)とディメンションテーブルへの外部キーが含まれています。
- いくつかのディメンションテーブル:各ディメンションテーブルは、単一のキーによってファクトテーブルに結合され、説明的な属性が含まれています。これらのテーブルは、簡素化と高速化のために高度に非正規化されています。
例:DateKey、ProductKey、StoreKey、QuantitySold、およびTotalRevenueのような列を持つFactSalesテーブル。DimDate、DimProduct、およびDimStoreテーブルに囲まれます。
スノーフレーク型スキーマは、ディメンションテーブルが複数の関連テーブルに正規化されるスター型スキーマの拡張です。たとえば、DimProductテーブルはDimProduct、DimBrand、およびDimCategoryテーブルに分割される場合があります。
推奨事項:スター型スキーマから始めます。クエリはより単純であり(結合が少ない)、最新のカラム型データベースは幅広く非正規化されたテーブルの処理に非常に効率的であるため、スノーフレーク型スキーマのストレージの利点は、多くの場合、追加の結合のパフォーマンスコストに比べてごくわずかです。
ステップ2:PythonでのETL/ELTパイプラインの構築
ETLプロセスは、データウェアハウスに供給するバックボーンです。ソースシステムからのデータの抽出、クリーンで一貫した形式への変換、分析モデルへのロードが含まれます。
Pandasを使用した簡単なPythonスクリプトで説明しましょう。生の注文のソースCSVファイルがあると想像してください。
# A simplified ETL example using Python and Pandas
import pandas as pd
# --- EXTRACT ---
print("Extracting raw order data...")
source_df = pd.read_csv('raw_orders.csv')
# --- TRANSFORM ---
print("Transforming data...")
# 1. Clean data
source_df['order_date'] = pd.to_datetime(source_df['order_date'])
source_df['product_price'] = pd.to_numeric(source_df['product_price'], errors='coerce')
source_df.dropna(inplace=True)
# 2. Enrich data - Create a separate Date Dimension
dim_date = pd.DataFrame({
'DateKey': source_df['order_date'].dt.strftime('%Y%m%d').astype(int),
'Date': source_df['order_date'].dt.date,
'Year': source_df['order_date'].dt.year,
'Quarter': source_df['order_date'].dt.quarter,
'Month': source_df['order_date'].dt.month,
'DayOfWeek': source_df['order_date'].dt.day_name()
}).drop_duplicates().reset_index(drop=True)
# 3. Create a Product Dimension
dim_product = source_df[['product_id', 'product_name', 'category']].copy()
dim_product.rename(columns={'product_id': 'ProductKey'}, inplace=True)
dim_product.drop_duplicates(inplace=True).reset_index(drop=True)
# 4. Create the Fact Table
fact_sales = source_df.merge(dim_date, left_on=source_df['order_date'].dt.date, right_on='Date')\
.merge(dim_product, left_on='product_id', right_on='ProductKey')
fact_sales = fact_sales[['DateKey', 'ProductKey', 'order_id', 'quantity', 'product_price']]
fact_sales['TotalRevenue'] = fact_sales['quantity'] * fact_sales['product_price']
fact_sales.rename(columns={'order_id': 'OrderCount'}, inplace=True)
# Aggregate to the desired grain
fact_sales = fact_sales.groupby(['DateKey', 'ProductKey']).agg(
TotalRevenue=('TotalRevenue', 'sum'),
TotalQuantity=('quantity', 'sum')
).reset_index()
# --- LOAD ---
print("Loading data into target storage...")
# For this example, we'll save to Parquet files, a highly efficient columnar format
dim_date.to_parquet('warehouse/dim_date.parquet')
dim_product.to_parquet('warehouse/dim_product.parquet')
fact_sales.to_parquet('warehouse/fact_sales.parquet')
print("ETL process complete!")
この単純なスクリプトは、コアロジックを示しています。実際のシナリオでは、このロジックを関数でラップし、Airflowのようなオーケストレーターでその実行を管理します。
ステップ3:OLAPエンジンの選択と実装
データがモデル化されロードされたら、OLAP操作を実行するためのエンジンが必要です。Pythonの世界では、主にROLAPアプローチに従って、いくつかの強力なオプションがあります。
アプローチA:軽量パワーハウス - DuckDB
DuckDBは、非常に高速でPythonで使いやすいインプロセス分析データベースです。SQLを使用してPandas DataFramesまたはParquetファイルを直接クエリできます。中小規模のOLAPシステム、プロトタイプ、およびローカル開発に最適な選択肢です。
これは、高性能ROLAPエンジンとして機能します。標準SQLを記述すると、DuckDBはデータファイルに対して非常に高速に実行します。
import duckdb
# Connect to an in-memory database or a file
con = duckdb.connect(database=':memory:', read_only=False)
# Directly query the Parquet files we created earlier
# DuckDB automatically understands the schema
result = con.execute("""
SELECT
p.category,
d.Year,
SUM(f.TotalRevenue) AS AnnualRevenue
FROM 'warehouse/fact_sales.parquet' AS f
JOIN 'warehouse/dim_product.parquet' AS p ON f.ProductKey = p.ProductKey
JOIN 'warehouse/dim_date.parquet' AS d ON f.DateKey = d.DateKey
WHERE p.category = 'Electronics'
GROUP BY p.category, d.Year
ORDER BY d.Year;
""").fetchdf() # fetchdf() returns a Pandas DataFrame
print(result)
アプローチB:クラウドスケールのタイタン - Snowflake、BigQuery、Redshift
大規模なエンタープライズシステムの場合、クラウドデータウェアハウスが標準的な選択肢です。Pythonはこれらのプラットフォームとシームレスに統合されます。ETLプロセスはデータをクラウドDWHにロードし、Pythonアプリケーション(たとえば、BIダッシュボードまたはJupyterノートブック)はそれをクエリします。
ロジックはDuckDBの場合と同じですが、接続とスケールが異なります。
import snowflake.connector
# Example of connecting to Snowflake and running a query
conn = snowflake.connector.connect(
user='your_user',
password='your_password',
account='your_account_identifier'
)
cursor = conn.cursor()
try:
cursor.execute("USE WAREHOUSE MY_WH;")
cursor.execute("USE DATABASE MY_DB;")
cursor.execute("""
SELECT category, YEAR(date), SUM(total_revenue)
FROM fact_sales
JOIN dim_product ON ...
JOIN dim_date ON ...
GROUP BY 1, 2;
""")
# Fetch results as needed
for row in cursor:
print(row)
finally:
cursor.close()
conn.close()
アプローチC:リアルタイムスペシャリスト - Apache DruidまたはClickHouse
大規模なストリーミングデータセット(リアルタイムユーザー分析など)でサブ秒のクエリレイテンシを必要とするユースケースの場合、DruidまたはClickHouseなどの特殊なデータベースが優れた選択肢です。これらはOLAPワークロード用に設計されたカラム型データベースです。Pythonは、データをストリーミングして、それぞれのクライアントライブラリまたはHTTP APIを介してクエリするために使用されます。
パート4:実践的な例 - ミニOLAPシステムの構築
これらの概念をミニプロジェクトに組み合わせてみましょう。インタラクティブな売上ダッシュボードです。これは、完全で、単純化されていますが、PythonベースのOLAPシステムを示しています。
当社のスタック:
- ETL:PythonとPandas
- データストレージ:Parquetファイル
- OLAPエンジン:DuckDB
- ダッシュボード:Streamlit(データサイエンス用の美しくインタラクティブなWebアプリを作成するためのオープンソースPythonライブラリ)
まず、パート3のETLスクリプトを実行して、warehouse/ディレクトリにParquetファイルを生成します。
次に、ダッシュボードアプリケーションファイルapp.pyを作成します。
# app.py - A Simple Interactive Sales Dashboard
import streamlit as st
import duckdb
import pandas as pd
import plotly.express as px
# --- Page Configuration ---
st.set_page_config(layout="wide", page_title="Global Sales Dashboard")
st.title("Interactive Sales OLAP Dashboard")
# --- Connect to DuckDB ---
# This will query our Parquet files directly
con = duckdb.connect(database=':memory:', read_only=True)
# --- Load Dimension Data for Filters ---
@st.cache_data
def load_dimensions():
products = con.execute("SELECT DISTINCT category FROM 'warehouse/dim_product.parquet'").fetchdf()
years = con.execute("SELECT DISTINCT Year FROM 'warehouse/dim_date.parquet' ORDER BY Year").fetchdf()
return products['category'].tolist(), years['Year'].tolist()
categories, years = load_dimensions()
# --- Sidebar for Filters (Slicing and Dicing!) ---
st.sidebar.header("OLAP Filters")
selected_categories = st.sidebar.multiselect(
'Select Product Categories',
options=categories,
default=categories
)
selected_year = st.sidebar.selectbox(
'Select Year',
options=years,
index=len(years)-1 # Default to the latest year
)
# --- Build the OLAP Query Dynamically ---
if not selected_categories:
st.warning("Please select at least one category.")
st.stop()
query = f"""
SELECT
d.Month,
d.MonthName, -- Assuming MonthName exists in DimDate
p.category,
SUM(f.TotalRevenue) AS Revenue
FROM 'warehouse/fact_sales.parquet' AS f
JOIN 'warehouse/dim_product.parquet' AS p ON f.ProductKey = p.ProductKey
JOIN 'warehouse/dim_date.parquet' AS d ON f.DateKey = d.DateKey
WHERE d.Year = {selected_year}
AND p.category IN ({str(selected_categories)[1:-1]})
GROUP BY d.Month, d.MonthName, p.category
ORDER BY d.Month;
"""
# --- Execute Query and Display Results ---
@st.cache_data
def run_query(_query):
return con.execute(_query).fetchdf()
results_df = run_query(query)
if results_df.empty:
st.info(f"No data found for the selected filters in year {selected_year}.")
else:
# --- Main Dashboard Visuals ---
col1, col2 = st.columns(2)
with col1:
st.subheader(f"Monthly Revenue for {selected_year}")
fig = px.line(
results_df,
x='MonthName',
y='Revenue',
color='category',
title='Monthly Revenue by Category'
)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Revenue by Category")
category_summary = results_df.groupby('category')['Revenue'].sum().reset_index()
fig_pie = px.pie(
category_summary,
names='category',
values='Revenue',
title='Total Revenue Share by Category'
)
st.plotly_chart(fig_pie, use_container_width=True)
st.subheader("Detailed Data")
st.dataframe(results_df)
これを実行するには、コードをapp.pyとして保存し、ターミナルでstreamlit run app.pyを実行します。これにより、インタラクティブなダッシュボードを備えたWebブラウザが起動します。サイドバーのフィルターを使用すると、ユーザーはOLAPの「スライス」および「ダイス」操作を実行でき、ダッシュボードはDuckDBを再クエリすることでリアルタイムで更新されます。
パート5:高度なトピックとベストプラクティス
ミニプロジェクトから本番システムに移行するにつれて、これらの高度なトピックを検討してください。
スケーラビリティとパフォーマンス
- 大規模なETLにDaskを使用する:ソースデータがマシンのRAMを超える場合は、ETLスクリプトでPandasをDaskに置き換えます。APIは非常によく似ていますが、Daskはアウトオブコア処理と並列処理を処理します。
- カラム型ストレージが重要:常にウェアハウスデータをApache ParquetまたはORCのようなカラム型形式で保存します。これにより、通常は幅広のテーブルからいくつかの列のみを読み取る必要がある分析クエリが大幅に高速化されます。
- パーティショニング:データレイク(S3やローカルファイルシステムなど)にデータを保存するときは、日付などの頻繁にフィルタリングされるディメンションに基づいてデータをフォルダーにパーティション分割します。たとえば:
warehouse/fact_sales/year=2023/month=12/。これにより、クエリエンジンは無関係なデータの読み取りをスキップできます。これは「パーティションプルーニング」として知られるプロセスです。
セマンティックレイヤー
システムが成長するにつれて、「アクティブユーザー」または「粗利益」の定義など、ビジネスロジックが複数のクエリとダッシュボードで繰り返されていることがわかります。セマンティックレイヤーは、ビジネスメトリックとディメンションの一元化された一貫性のある定義を提供することで、これを解決します。dbt(Data Build Tool)のようなツールは、これに非常に適しています。dbtはPythonツール自体ではありませんが、Pythonが調整するワークフローに完全に統合されます。dbtを使用してスター型スキーマをモデル化し、メトリックを定義します。その後、Pythonを使用してdbtの実行を調整し、結果として得られるクリーンなテーブルで高度な分析を実行できます。
データガバナンスと品質
ウェアハウスは、その中のデータと同じくらい優れています。データ品質チェックをPython ETLパイプラインに直接統合します。Great Expectationsのようなライブラリを使用すると、データに関する「期待」を定義できます(たとえば、customer_idはnullであってはならず、revenueは0から1,000,000の間である必要があります)。その後、ETLジョブは、着信データがこれらのコントラクトに違反した場合、失敗するか、警告を発することができます。これにより、不良データがウェアハウスを破損するのを防ぎます。
結論:コードファーストアプローチの力
Pythonは、データウェアハウジングとビジネスインテリジェンスの状況を根本的に変えました。洗練された分析システムをゼロから構築するための柔軟で強力なベンダーニュートラルなツールキットを提供します。Pandas、Dask、SQLAlchemy、およびDuckDBのようなクラス最高のライブラリを組み合わせることで、スケーラブルで保守可能な完全なOLAPシステムを作成できます。
ジャーニーは、スター型スキーマのようなデータモデリングの原則をしっかりと理解することから始まります。そこから、堅牢なETLパイプラインを構築してデータを整形し、スケールに適したクエリエンジンを選択し、インタラクティブな分析アプリケーションを構築することもできます。このコードファーストのアプローチは、多くの場合、「モダンデータスタック」の中核となる原則であり、分析の力を開発者とデータチームの手に直接委ね、組織のニーズに完全に合わせたシステムを構築できるようにします。