Dataikuでノンコーダー向けアプリを作る![後編]

Dataikuでノンコーダー向けアプリを作る![後編] | Tableau-id Press -タブロイド-
dataiku_logo

前編中編に引き続き、「Dataikuでノンコーダー向けアプリを作る!」の後編です。

動的に変わるダッシュボードを作る

Robynは中間結果として良さげなモデルの結果をpngファイルで出力してくれます。



Robyn内のダミーデータで試した結果例。FacebookのROIが高く出てるのはたまたまだよね、うん。

このペライチ(Robyn内ではonepagerと呼ばれています)を並べて比較するためのダッシュボードを作成します。

ダッシュボードはインサイトを並べています。
ダッシュボードにインサイトの枠を作って置き、Rコードからインサイトを上書きしています。

良さげなモデルの数はデータによって変わるので、とりあえず8つ枠を置いています。
Robyn内のダミーデータで実行したときに8つ候補があがり(同じデータでも変数の指定の仕方で変わる可能性があり)、
自分で作成したデータなどで試しても8つを超えてくることがなかったのでとりあえず8つです。
でも8つに満たない場合は前のデータのが残ってしまうのでインサイトを真っ白にした方がいいなど、残課題があります。
今回の結果はどれだ、というのは最適化をするモデルを選択する際に候補としてプルダウンに入ってくるか、
で一応判断できます(^_^;) 仕様としてはあんまりよくないですね。

1つ目のRコードにペライチをinsightにするコードを追加します。

# 候補のモデル番号を取得
df2 <- OutputCollect[["clusters"]]$data %>%
 filter(top_sol == "TRUE")
ids <- df2$solID

# Set the size of the plot device
options(repr.plot.width = 10, repr.plot.height = 10)

# Loop over the IDs
for (i in seq_along(ids)) {
 id <- ids[i]
 # Print a message indicating which ID is being processed
 message("Processing ID number ", i, ": ", id)

 dev.new()

 output <- robyn_onepagers(InputCollect, OutputCollect, select_model = id)
 image <- output[[id]] # Access the image using the ID

 # Check the device before calling the function
 print(dev.cur())

 # Print the image
 dkuSaveGgplotInsight(paste0("onepager_", i), gg = image, label = NULL, scale = 1, width = 10, height = 10, dpi = 300)

 # Check the device after calling the function
 print(dev.cur())

 # Wait for a second to ensure the plot is completely drawn
 Sys.sleep(10)

 # Close the graphics device if it's not the null device
 if (dev.cur() != 1) {
  try({
   dev.off()
  }, silent = TRUE)
 }
}


デモコードには無い部分をGitHubを漁り、Dataikuのサポートに駆け込み、ChatGPT先生に聞いて追加しました。
繰り返し処理をかけようとすると2枚目のペライチでどうしても止まってしまい、サポートの方に助けていただきました。
中間結果を一覧にして見たい、という要望に対してインサイト→ダッシュボードで更新できる、というのも
サポートの方に教えていただきました。

コードを追加し、[Save back to recipe]をしてからRコードレシピをRUNします。
…メニューの一つ左側のアイコンからInsightsを選択します。


Static insightという種類のインサイトがRコードで生成されました!
インサイトを配置する先のダッシュボードを作成しましょう。
インサイト画面を表示する際に選択したメニューで、Insightsの上にDashboardがあるので、クリックします。


最初から1つダッシュボードが存在しています。これをそのまま使っても良し、新しく作成しても良いです。
今回はそのまま使います。編集画面を開きます。


赤枠の+をクリックしてタイルを追加します。



いろいろダッシュボードに追加できるものの種類が出てきます。Static insightを選択します。


全部追加しちゃいましょう。右のアイコンが赤になったら追加されています。
[DONE]でウィンドウを閉じて、タイルの大きさ・位置をドラッグで調整します。
終わったら右上の[SAVE]で忘れずに保存します。

これでApplication designerからDashboardを見る機能を追加できます!

Robynコード(1つ目)の修正

Robyn内のダミーデータからインプットしたデータに付け替え、Project Variableを使用したり、
インプットしたデータに対応(日付範囲やメディアの種類はデータごとに変わる)できるようにコードを修正していきます。

まずインプットデータを付け替えます。
dt_simulated_weeklyを使用している箇所をコメントアウトし、データの定義を変更します。
Rコードレシピ自体のInputの設定も変更する必要があります。

# Recipe inputs
robyn_dummy <- dkuReadDataset("robyn_daily_new_dummy", samplingMethod="head", nbRows=100000)
jp_holiday <- dkuReadDataset("jp_holiday", samplingMethod="head", nbRows=100000)

Project Variableを追加します。
シナリオを組む前はベタ打ちで値を入れておくと動作確認ができるのでお勧めです。
Robynはもっとたくさんパラメータをいじることができるのですが、今回のプロジェクトでは以下の種類を用意しました。
※元のプロジェクトとアプリのテストインスタンスの両方でProject Variableを定義する必要があります。

{
"tv_cost": "",
"tv_conversion": "",
"web_cost": "",
"web_conversion": "",
"dm_cost": "",
"dm_conversion": "",
"other_ad1_cost": "",
"other_ad1_conversion": "",
"other_ad2_cost": "",
"other_ad2_conversion": "",
"objective": "",
"date": "",
"exogenous_variable": "",
"select_model": "",
"scenario": "max_response",
"total_budget": 10000000000
}

Project VariableをRコード内で使用するために修正します。

# 外生変数exogenous_variable <- dkuCustomVariable("exogenous_variable")

# date 列の最小値を取得 
min_date <- min(robyn_dummy[[dkuCustomVariable("date")]], na.rm = TRUE) 
# date 列の最大値を取得 
max_date <- max(robyn_dummy[[dkuCustomVariable("date")]], na.rm = TRUE) 

# variableの入力を受け取り、整形する 
cost_columns <- c(dkuCustomVariable("tv_cost"), 
  dkuCustomVariable("web_cost"), 
  dkuCustomVariable("dm_cost"), 
  dkuCustomVariable("other_ad1_cost"), 
  dkuCustomVariable("other_ad2_cost")) 
cost_columns_cleaned <- cost_columns[cost_columns != ''] 
cost_columns_list <- paste(sprintf('%s', cost_columns_cleaned), collapse = ", ") 
conversion_columns <- c(dkuCustomVariable("tv_conversion"), 
  dkuCustomVariable("web_conversion"), 
  dkuCustomVariable("dm_conversion"), 
  dkuCustomVariable("other_ad1_conversion"), 
  dkuCustomVariable("other_ad2_conversion")) 
conversion_columns_cleaned <- conversion_columns[conversion_columns != ''] 
conversion_columns_list <- paste(sprintf('%s', conversion_columns_cleaned), collapse = ", ") 

# コストとCVの形を直す 
convert_to_vector_simple <- function(variable_str) {
  # カンマで分割してベクトルを作成  
  as.vector(unlist(strsplit(trimws(variable_str), ",\\s*"))) } 
cost_converted_vector <- convert_to_vector_simple(cost_columns_list) 
conversion_converted_vector <- convert_to_vector_simple(conversion_columns_list) 
# 外生変数の形を直す 
convert_to_vector <- function(variable_str) { 
 # 文字列から不要な文字([、]、および")を取り除きます 
 cleaned_str <- gsub('"', '', variable_str) 
 cleaned_str <- gsub('\\[|\\]', '', cleaned_str) 
 # カンマで分割してベクトルを作成 
 as.vector(unlist(strsplit(cleaned_str, ",\\s*"))) } 
variable_str <- exogenous_variable 
exogenous_variable_vector <- convert_to_vector(variable_str)

Project Variableを取得しました。
外生変数は複数選択を許可しているのでProject Variableへの入力形式が異なるため、ベクトル化も別で行いました。

{
"tv_cost": "tv_S",
"tv_conversion": "tv_S",
"web_cost": "facebook_S",
"web_conversion": "facebook_I",
"dm_cost": "print_S",
"dm_conversion": "print_S",
"other_ad1_cost": "ooh_S",
"other_ad1_conversion": "ooh_S",
"other_ad2_cost": "search_S",
"other_ad2_conversion": "search_clicks_P",
"objective": "revenue",
"date": "DATE",
"exogenous_variable": [
  "events",
  "competitor_sales_B"
],
"select_model": "2_230_7",
"scenario": "max_response",
"total_budget": 10000000000
}


Project Variableの設定後イメージです。
Application designerを組んだ後はUIから設定した内容が↑のイメージのように更新されます。

Project VariableをInputcollectに入力するように変更します。↓

InputCollect <- robyn_inputs(
 dt_input = robyn_dummy,
 dt_holidays = jp_holiday,
 date_var = dkuCustomVariable("date"), # date format must be "2020-01-01"
 dep_var = dkuCustomVariable("objective"), # there should be only one dependent variable
 dep_var_type = "conversion", # "revenue" (ROI) or "conversion" (CPA)
 prophet_vars = c("trend", "season", "holiday"), # "trend","season", "weekday" & "holiday"
 prophet_country = "JP", # input country code. Check: dt_prophet_holidays
 context_vars = exogenous_variable_vector, # e.g. competitors, discount, unemployment etc
 paid_media_spends = cost_converted_vector, # mandatory input
 paid_media_vars = conversion_converted_vector, # mandatory.
 # paid_media_vars must have same order as paid_media_spends. Use media exposure metrics like
 # impressions, GRP etc. If not applicable, use spend instead.
 # organic_vars = "newsletter", # marketing activity without media spend
 # factor_vars = exogenous_variable_vector, # force variables in context_vars or organic_vars to be categorical
 # organic_vars あるいは context_vars で指定された変数のうち、どの変数が因子として強制されるべきかを指定する。
 window_start = min_date,
 window_end = max_date,
 adstock = "geometric" # geometric, weibull_cdf or weibull_pdf.
)
# データセット内に変数が存在するか確認する関数
variable_exists <- function(dataset, variable_name) {
 return(variable_name %in% names(dataset))
}

# データセットに存在する変数に対してだけhyperparametersを設定する関数
set_hyperparameters <- function(dataset) {
 potential_vars <- c(dkuCustomVariable("tv_cost"),
 dkuCustomVariable("web_cost"),
 dkuCustomVariable("dm_cost"),
 dkuCustomVariable("other_ad1_cost"),
 dkuCustomVariable("other_ad2_cost"))

 hyperparameters <- list()

 for (var in potential_vars) {
  if (variable_exists(dataset, var)) {
   hyperparameters[paste0(var, "_alphas")] <- c(0.5, 3)
   hyperparameters[paste0(var, "_gammas")] <- c(0.3, 1)
   hyperparameters[paste0(var, "_thetas")] <- c(0.1, 0.4) # 一般的な値として0.1, 0.4を使用。必要に応じて調整してください。
  }
 }

 # train_size は常に含める
 hyperparameters$train_size <- c(0.5, 0.8)

 return(hyperparameters)
}

hyperparameters_result <- set_hyperparameters(robyn_dummy)
print(hyperparameters_result)

#### 2a-3: Third, add hyperparameters into robyn_inputs()

InputCollect <- robyn_inputs(InputCollect = InputCollect, hyperparameters = hyperparameters_result)

ハイパーパラメータはメディアの数必要で、それぞれに調整することもできるのですが、
今回はすべてのメディアで同じにしてしまいました。
アプリの作成を優先したので無視してしまった変数・固定してしまった変数もあります。ご了承ください。

インプットしたデータを用いてProject Variableを更新し、Rコードレシピで使えるように修正しました。
Application designerで各Variableを更新するためのUIを作成したら完成です!
(数が多いのと並び替えができない・し辛いので結構大変だった…)

前編中編・後編と長くなりましたが、お付き合いいただきありがとうございました!
(続編・番外編も出るかも…)