文本清理和格式化
如何使用自定义表达式清理不一致、无结构或空白文本。
假设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. | |
我们想要
- 清理和合并主菜和配菜值,以便我们可以统计最受欢迎的菜品。
- 处理某些方式无效的回复(例如,多个主菜或主菜不在我们的菜单上)。
- 跟踪提交了缺失信息回复的客人。
总体而言,我们希望最终得到一个如下所示的表格(向右滚动查看完整表格)
| 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
函数来检查每个回复中的有效菜单选项。
为了在主菜列中搜索“牛肉炖饭”,我们将创建一个自定义列,使用正则表达式模式(?i)(beef tibs)
。此正则表达式模式执行不区分大小写的检查,以查看“beef tibs”是否出现在回复中的任何位置。
使用以下方法创建牛肉自定义列
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. | |
然后,我们想要在主菜列中搜索有效的值“鹰嘴豆炖菜”。
创建鹰嘴豆列
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. | |
合并来自不同列的值
接下来,我们将创建一个名为主菜(清洁)的列,该列将汇总每个客人回复的有效主菜。我们希望设置一些逻辑,以便如果主菜包含
- 单个有效选项(牛肉炖饭或鹰嘴豆炖菜),则将那个选项填入主菜(清洁)。
- 多个有效选项,则将第一个(最左边的)有效选项放入主菜(清洁)。
- 没有有效选项,则将空白值(空字符串)填入主菜(清洁)。
为了创建主菜(清洁),我们将使用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
函数在配菜列中搜索并返回有效值。
创建英吉拉自定义列
regexextract([Side], "(?i)injera")
和蔬菜自定义列
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 | | | |
然后,使用带有lower
函数的coalesce
函数来处理人们可能输入的部分、多个或无有效配菜选项的情况,并将所有值转换为小写
创建配菜(清洁)自定义列
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 | | | | |
合并来自不同列的值
最后,我们想要通过检查每个场景来生成完整的订单
- 如果主菜(清洁)和配菜(清洁)都包含有效的选项,则返回“主菜加配菜”。
- 如果只有一个有效的选项,则返回“主菜仅”或“配菜仅”。
- 如果没有有效选项,则订单为空(返回空字符串)。
要检查列是否非空,我们将使用isempty
函数。
例如,检查主菜(清洁)是否为空
isempty([Main (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的顺序很重要,因为
case
函数按顺序评估每个表达式,并在找到第一个有效的case后停止。- 如果您交换了第一个case和第二个case,表达式会立即确认配菜(清洁)为空并返回“主菜仅”,而无需检查是否主菜(清洁)也为空。
最后,为了填写每个顾客的最终订单,我们将使用concat
函数来将来自主菜(清洁)和配菜(清洁)的值与其他单词(包括空格)连接起来。
使用以下方式创建订单列:
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. | | | | |
对空值行进行标记
假设我们想要添加一个名为跟进?的列来跟踪缺少有效主菜、配菜或两者都缺少的订单。这意味着我们需要检查是否任何以下列是空的:订单、主菜(清洁)或配菜(清洁)。
我们可以使用isempty
函数和OR
运算符来返回“是”如果三个列中的任何一个为空,如果所有列都填充了有效数据,则返回“否”。
使用以下方式创建跟进?
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 |
最佳实践和建议
在本教程中,我们在需要提取、合并或标记文本数据时创建了新的自定义列。我们还组合了简单的函数(如lower
和isempty
)和其他函数。通常,我们建议每次使用具有多个参数的函数(如case
、regexextract
和coalesce
)时创建一个新的自定义列,因为
- 您可以确认您的表达式是否按预期工作。
- 逻辑更易于阅读和更新。
如果您习惯于使用SQL、电子表格或Python等工具中的函数,请查看自定义表达式文档中的“相关函数”部分。例如,您可以使用case
的相关函数学习如何将if-then逻辑转换为Metabase表达式。相关函数。