清理和格式化文字

如何使用自訂運算式來清理不一致、非結構化或空白的文字。

假設 Metabase 想要為我們可愛的社群舉辦一場晚宴。主菜方面,我們有牛肉提比斯或鷹嘴豆燉菜可供選擇,配菜方面,我們有英吉拉或烤蔬菜。我們已發送包含菜單選項的調查,讓大家可以告訴我們他們想吃什麼。

不幸的是,我們忘記在表單上設定資料驗證,因此收到的回覆看起來像這樣

| Response ID | Main                                | Side                    |
|-------------|-------------------------------------|-------------------------|
| 1           | beef tibs                           | injera                  |
| 2           | chickpea stew                       | grilled vegetables      |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE |                         |
| 4           |                                     | Grilled Vegetables      |
| 5           | Surprise me.                        |                         |

我們想要

  1. 清理並合併主菜和配菜值,以便我們可以計算最受歡迎的餐點。
  2. 處理以某種方式無效的回覆(例如多個主菜,或菜單上不存在的主菜)。
  3. 追蹤提交資訊遺漏回覆的客人。

總體而言,我們希望最終得到一個看起來像這樣的表格(向右捲動以檢視完整表格)

| Response ID | Main                                | Side                    | Order                                 | Follow up? |
|-------------|-------------------------------------|-------------------------|---------------------------------------|------------|
| 1           | beef tibs                           | injera                  | beef tibs with injera                 | no         |
| 2           | chickpea stew                       | grilled vegetables      | chickpea stew with grilled vegetables | no         |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE |                         | beef tibs only                        | yes        |
| 4           |                                     | Grilled Vegetables      | grilled vegetables only               | yes        |
| 5           | Surprise me.                        |                         |                                       | yes        |

搜尋和擷取文字

假設唯一有效的主菜選項是牛肉提比斯和鷹嘴豆燉菜。我們可以使用 regexextract 函數來檢查每個回覆中是否有有效的菜單選項。

若要在主菜欄中搜尋「beef tibs」值,我們將使用 regex 模式 (?i)(beef tibs) 建立自訂欄。此 regex 模式會執行不區分大小寫的檢查,以查看「beef tibs」是否出現在回覆中的任何位置。

使用下列項目建立 Beef 自訂欄

regexextract([Main], "(?i)(beef tibs)")

您應該會得到輸出

| Response ID | Main                                | Beef      |
|-------------|-------------------------------------|-----------|
| 1           | beef tibs                           | beef tibs |
| 2           | chickpea stew                       |           |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE | BEEF TIBS |
| 4           |                                     |           |
| 5           | Surprise me.                        |           |

然後,我們想要在主菜欄中搜尋有效值「chickpea stew」。

建立 Chickpea

regexextract([Main], "(?i)(chickpea stew)")

以及輸出

| Response ID | Main                                | Chickpea      |
|-------------|-------------------------------------|---------------|
| 1           | beef tibs                           |               |
| 2           | chickpea stew                       | chickpea stew |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE | CHICKPEA STEW |
| 4           |                                     |               |
| 5           | Surprise me.                        |               |

合併不同欄中的值

接下來,我們將建立一個名為 Main (Clean) 的欄,它將合併每位客人的回覆的有效主菜。我們想要設定一些邏輯,以便在 Main 包含下列項目時

  • 單一有效選項(牛肉提比斯或鷹嘴豆燉菜),然後以該選項填寫 Main (Clean)
  • 多個有效選項,然後將第一個(最左邊)有效選項放入 Main (Clean)
  • 沒有有效選項,然後以空白值(空字串)填寫 Main (Clean)

若要建立 Main (Clean),我們將使用 coalesce 函數來處理上面列出的三種情況,並將整個內容包裝在 lower 函數中,以將所有內容標準化為小寫。

lower(coalesce([Beef],[Chickpea],""))

這應該會給我們輸出(向右捲動以檢視完整表格)

| Response ID | Main                                | Beef      | Chickpea      | Main (Clean)   |
|-------------|-------------------------------------|-----------|---------------|----------------|
| 1           | beef tibs                           | beef tibs |               | beef tibs      |
| 2           | chickpea stew                       |           | chickpea stew | chickpea stew  |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE | BEEF TIBS | CHICKPEA STEW | beef tibs      |
| 4           |                                     |           |               |                |
| 5           | Surprise me.                        |           |               |                |

擷取文字並合併結果

我們將以與主菜欄相同的方式處理配菜欄。首先,使用 regexextract 函數來搜尋並傳回配菜欄中的有效值。

建立 Injera 自訂欄

regexextract([Side], "(?i)injera")

Vegetables 自訂欄

regexextract([Side], "(?i)(grilled vegetables)")

以取得輸出

| Response ID | Side               | Injera | Vegetables         |
|-------------|--------------------|--------|--------------------|
| 1           | injera             | injera |                    |
| 2           | grilled vegetables |        | grilled vegetables |
| 3           |                    |        |                    |
| 4           | Grilled Vegetables |        | Grilled Vegetables |
| 5           |                    |        |                    |

然後,使用 coalesce 函數lower 函數來處理人們放入部分、多個或沒有有效配菜選項的情況,並將所有值轉換為小寫

建立 Side (Clean) 自訂欄

lower(coalesce([Injera],[Vegetables], ""))

以取得

| Response ID | Side               | Injera | Vegetables         | Side (Clean)       |
|-------------|--------------------|--------|--------------------|--------------------|
| 1           | injera             | injera |                    | injera             |
| 2           | grilled vegetables |        | grilled vegetables | grilled vegetables |
| 3           |                    |        |                    |                    |
| 4           | Grilled Vegetables |        | Grilled Vegetables | grilled vegetables |
| 5           |                    |        |                    |                    |

合併不同欄中的值

最後,我們想要透過檢查每個情境來產生完整訂單

  • 如果 Main (Clean)Side (Clean) 都包含有效選項,則傳回「主菜加配菜」。
  • 如果只有一個有效選項,則傳回「僅主菜」或「僅配菜」。
  • 如果沒有有效選項,請將訂單留白(傳回空字串)。

若要檢查欄是否為非空白,我們將使用 isempty 函數

例如,若要檢查 Main (Clean) 是否為空白

isempty([Main (Clean)])

若要檢查 Main (Clean)Side (Clean) 是否為空白,您可以使用 AND 來合併運算式

isempty([Main (Clean)]) AND isempty([Side (Clean)])

isempty 目前僅在另一個函數內運作,因此我們需要將我們的每個檢查放入 case 函數中。我們將把預留位置文字作為目前的輸出

case(
    (isempty([Main (Clean)]) AND isempty([Side (Clean)])), "",
    isempty([Side (Clean)]), "main only",
    isempty([Main (Clean)]), "side only",
    "main with side"
)

請注意,案例的順序很重要,因為

  • case 函數會依序評估每個運算式,並在找到第一個有效案例時停止。
  • 如果您將第一個案例與第二個案例交換,則運算式會確認 Side (Clean) 為空白,並立即傳回「僅主菜」,而不會檢查 Main (Clean) 是否也為空白。

最後,若要填寫每位客人的最終訂單,我們將使用 concat 函數來連結 Main (Clean)Side (Clean) 中的值與其他字詞(包括空格)。

使用下列項目建立 Order

case(
    (isempty([Main (Clean)]) AND isempty([Side (Clean)])), "",
    isempty([Side (Clean)]), concat([Main (Clean)], " only"),
    isempty([Main (Clean)]), concat([Side (Clean)], " only"),
    concat([Main (Clean)], " with ", [Side (Clean)])
)

總體而言,這將為我們提供一組格式化的欄,如下所示(向右捲動以檢視完整表格)

| Response ID | Main                                | Side               | Main (Clean)    | Side (Clean)       | Order                                 |
|-------------|-------------------------------------|--------------------|-----------------|--------------------|---------------------------------------|
| 1           | beef tibs                           | injera             | beef tibs       | injera             | beef tibs with injera                 |
| 2           | chickpea stew                       | grilled vegetables | chickpea stew   | grilled vegetables | chickpea stew with grilled vegetables |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE |                    | beef tibs       |                    | beef tibs only                        |
| 4           |                                     | Grilled Vegetables |                 | grilled vegetables | grilled vegetables only               |
| 5           | Surprise me.                        |                    |                 |                    |                                       |

標記具有空白值的列

假設我們想要新增一個名為 Follow up? 的欄,以追蹤遺漏有效主菜、配菜或兩者的訂單。這表示我們需要檢查 OrderMain (Clean)Side (Clean) 中是否有任何欄為空白。

我們可以將 isempty 函數OR 運算子結合使用,以便在三個欄中的任何一個欄為空白時傳回「是」,而在所有欄都填寫有效資料時傳回「否」。

使用下列項目建立 Follow up?

case(( isempty([Order])
    OR isempty([Main (Clean)])
    OR isempty([Side (Clean)])), "yes", "no")

最終結果(向右捲動以檢視完整表格)

| Response ID | Main                                | Side                    | Order                                 | Follow up? |
|-------------|-------------------------------------|-------------------------|---------------------------------------|------------|
| 1           | beef tibs                           | injera                  | beef tibs with injera                 | no         |
| 2           | chickpea stew                       | grilled vegetables      | chickpea stew with grilled vegetables | no         |
| 3           | BEEF TIBS WITH CHICKPEA STEW PLEASE |                         | beef tibs                             | yes        |
| 4           |                                     | vegetables              | grilled vegetables                    | yes        |
| 5           | Surprise me.                        |                         |                                       | yes        |

最佳實務與提示

在本教學課程中,每次我們需要擷取、合併或標記文字資料時,都會建立新的自訂欄。我們也將較簡單的函數(例如 lowerisempty)與其他函數結合使用。一般而言,我們建議每次在使用具有多個參數的函數(例如 caseregexextractcoalesce)時,都建立新的自訂欄,因為

  • 您可以確認您的運算式是否如預期般運作。
  • 邏輯更容易閱讀和更新。

而且,如果您習慣使用其他工具(例如 SQL、試算表或 Python)中的函數,請查看自訂運算式文件中的相關函數章節。例如,您可以瞭解如何使用 case 的相關函數將 if-then 邏輯轉換為 Metabase 運算式。

延伸閱讀