Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Try using dbtplyr

Try using dbtplyr

dbtplyr を使ってみる
小坪琢人 (@airspace_nobo)

※この資料は以下イベントで発表した内容です。
https://japanr.connpass.com/event/334154/
#JapanR

More Decks by LINEヤフーTech (LY Corporation Tech)

Other Decks in Technology

Transcript

  1. 思い出話 Tokyo.R での過去の発表歴 ❏ Use R from python @Tokyo.R #80

    ❏ How to predict the wind direction of tomorrow @Tokyo.R #71 Tokyo.R #71 以来のHadley wickham との2度目の共演 ? ❏ (当時)のヤフーさんが会場提供をしていたので、ここのオフィスで 発表した 。そしてまたここのオフィスが会場に!
  2. 本題へ ❏ 最近業務で使い始めた dbt というモダンなデータパイプライン ツールにdplyr の記法を組み込む packageを見つけたので紹介 する ※

    dbtというツールを初めて見る人も多いと思うので、 ”こんなことが できるんだ ”くらいの感じで理解していただければと思います!
  3. 発表の流れ ❏ データモデリングについて紹介 ❏ dbt について紹介 ❏ dbt で何が解決できるか ?

    ❏ SQLを書く時のお悩み ❏ macro で解決できること ❏ dbtplyr で解決できること ❏ まとめ
  4. dbtとは ❏ 左記のqueryをstg_orgers.sql として作成して dbt を動かすと、設 定から適切な DB、tableを参照される ❏ queryから、「raw_ordersを参照して

    stg_ordersを作成した」とい う処理の依存関係を可視化できる select id as order_id, user_id as customer_id, order_date, status from {{ source('main', 'raw_orders') }} select id as order_id, user_id as customer_id, order_date, status from "tutorial"."main"."raw_orders"
  5. SQLを書く時のお悩み ❏ 関数的なものがないので規則性に複数のカラムを処理する時にも 繰り返し書く必要がある ❏ 繰り返し書くことで、 typoなどが起きる可能性も高まる (例; numを nuと書き間違える

    ) select avg(num_a) as avg_num_a, avg(num_b) as avg_num_b, avg(num_c) as avg_nu_c, from data data |> summarise(across(starts_with('num'), ~mean(.x, na.rm=TRUE), .names = "avg_{.col}"))
  6. macroで解決できること ❏ payment_methodごとに横持ちで集計したい時、 SQLだと繰り返 して書く必要がある with order_payments as ( select

    order_id, sum(case when payment_method = 'credit_card' then amount else 0 end) as credit_card_amount, sum(case when payment_method = 'coupon' then amount else 0 end) as coupon_amount, sum(case when payment_method = 'bank_transfer' then amount else 0 end) as bank_transfer_amount, sum(case when payment_method = 'gift_card' then amount else 0 end) as gift_card_amount, sum(amount) as total_amount
  7. macroで解決できること ❏ dbt のmacroを使うとfor loopや処理を一般化できる {% set payment_methods = ['credit_card',

    'coupon', 'bank_transfer', 'gift_card'] %} with order_payments as ( select order_id, {% for payment_method in payment_methods -%} sum(case when payment_method = '{{ payment_method }}' then amount else 0 end) as {{ payment_method }}_amount, {% endfor -%} sum(amount) as total_amount
  8. macroで解決できること ❏ macroを関数とし、他の queryでも利用可能 (例: macro/util.sql) {% macro get_payment_methods() %}

    ['credit_card', 'coupon', 'bank_transfer', 'gift_card'] {% endmacro %} {% macro calculate_method_amounts(payment_methods, amount_column='amount', payment_method_column='payment_method') %} {% for payment_method in payment_methods -%} sum(case when {{ payment_method_column }} = '{{ payment_method }}' then {{ amount_column }} else 0 end) as {{ payment_method }}_amount, {% endfor -%} {% endmacro %}
  9. dbtplyrで解決できること ❏ acrossを使って繰り返しの集計をまとめる {% set cols = dbtplyr.get_column_names( ref('customers') )

    %} {% set cols_num = dbtplyr.starts_with('num_', cols) %} select customer_id, {{ dbtplyr.across(cols_num, "sum({{var}}) as {{var}}_tot") }} from {{ ref('customers') }} group by 1 select customer_id, sum(num_total_orders) as num_total_orders_tot, sum(num_total_amount) as num_total_amount_tot from "tutorial"."main"."customers" group by 1
  10. dbtplyrで解決できること ❏ acrossを使って繰り返しの変換をまとめる {% set cols = dbtplyr.get_column_names( ref('customers') )

    %} {% set cols_date = dbtplyr.starts_with('date_', cols) %} select customer_id, {{ dbtplyr.across(cols_date, "cast({{var}} as timestamp) as {{var}}")}} from {{ ref('customers') }} select customer_id, cast(date_first_order as date) as date_first_order, cast(date_most_recent_order as date) as date_most_recent_order from "tutorial"."main"."customers"
  11. dbtplyrで解決できること ❏ for loop と組み合わせて where条件にも使える {% set cols_date =

    dbtplyr.starts_with('date_', cols) %} ... where {% for col in cols_date -%} {{col}} >= cast('2018-02-01' as date) or {% endfor %} FALSE ... where date_first_order >= cast('2018-02-01' as date) or date_most_recent_order >= cast('2018-02-01' as date) or FALSE
  12. dbtplyrではdplyrの下記の記法が使える starts_with(string, relation or list) ends_with(string, relation or list) contains(string,

    relation or list) not_contains(string, relation or list) one_of(string_list, relation or list) not_one_of(string_list, relation or list) matches(string, relation) everything(relation) where(fn, relation) if_any(var_list, script_string) if_all(var_list, script_string)
  13. まとめ ❏ Data Engineering (dbt) × Data Science (dplyr) の活用について

    紹介した ❏ SQLの中で使っている処理はシンプルかつ便利な機能なので、 dplyrがdbtの世界でも広まっていくかもしれない ❏ dbt Advent Calendar 2024 でもdbtの導入手順なども含めてもう 少し詳しく書く予定です
  14. 参考文献 dbtplyr: Bringing Column-Name Contracts from R to dbt -

    posit::conf(2023) What, exactly, is dbt? Apply a function (or functions) across multiple columns Rが生産性を高める 〜データ分析ワークフロー効率化の実践