---
schema_version: "1"
id: 1389f66a030e3f15b72333bf735e373bd7e9f09bdecc5345b6099445082d3f69
title: Tableau Desktop 中包含第二个日期的分析
collection: tableau
version: next
locale: zh-cn
portal_url: https://docs.salesforce.com/tableau/next/zh-cn/prep/prep_tutorial_2nddateB.md
canonical_url: https://help.tableau.com/v0.0/prep/zh-cn/prep_tutorial_2nddateB.htm
source_site: help.tableau.com
content_type: documentation
content_hash: 0bebc3168b177b3303e3f6e822eeac9196fa212957ff0c5093ae582dbb296817
description: "tutorial: analyze the second-date data set in Tableau Desktop."
---
[跳转到主要内容](#main-content)

# Tableau Desktop 中包含第二个日期的分析

这是教程的第二个阶段，并且假定第一个阶段[使用 Tableau Prep 查找第二个日期](https://help.tableau.com/v0.0/prep/zh-cn/prep_tutorial_2nddateA.htm)已完成。

在第一个阶段中，我们采用原始数据集并对其进行调整来回答以下问题：

1. 每个司机第一次和第二次违章相隔多长时间（以天数为单位）？
2. 比较第一次和第二次违章的罚款金额。这些金额是否相关？
3. 总体而言，哪个司机支付的罚款最多？谁支付的最少？
4. 有多少司机有过多种类型的违章？
5. 从未上过交通法规学习班的司机的平均罚款金额是多少？

我们现在探讨这些问题时，可以清晰地发现我们创建的第一个数据结构有一些优缺点。我们将返回到 Tableau Prep Builder 中并进行一些额外的重新调整，然后看看会对 Tableau Desktop 中同样的分析产生怎样的影响。最后，我们将探讨 Tableau Desktop 独有的将详细级别 (LOD) 表达式与原始数据结合使用进行分析的方法。

本教程的目标是在一个真实场景的上下文中呈现各种概念并演练各个选项 — 而不是规定式地确定哪一个是最好的。最后，您应该会对数据结构对计算和分析的影响有更深的认识，并更加熟悉 Tableau Prep 的各项功能以及 Tableau Desktop 中的计算。

**注意：**为了完成本教程中的任务，您需要安装 Tableau Prep Builder 并根据需要安装 Tableau Desktop，并下载相关数据。  
  
若要在继续学习本教程之前安装 Tableau Prep 和 Tableau Desktop，请参见 [Tableau Desktop 和 Tableau Prep 部署指南](https://help.tableau.com/current/desktopdeploy/zh-cn/desktop_deploy_welcome.htm#)。否则，您可以下载 [Tableau Prep](https://www.tableau.com/zh-cn/products/prep/download) 和 [Tableau Desktop](https://www.tableau.com/zh-cn/products/desktop/download) 免费试用版。  
  
数据集是在第一个阶段中构建的 [Driver Infractions.tflx](https://mkt.tableau.com/PrepTutorials/Driver%20Infractions.tflx) 的输出。

## Tableau Desktop 中的分析

既然我们已经配置了数据，我们就将其引入 Tableau Desktop。我们可以轻松地回答一些问题，但其他问题会涉及一些（或很多）计算。请尝试下面的问题；如果遇到困难，您可以展开每个问题来了解有关如何继续的基本信息。

**注意：**您可以下载工作簿 [Driver Infractions.twbx](https://mkt.tableau.com/PrepTutorials/Driver%20Infractions.twbx)，在上下文中探讨解决方案。请记住，可以通过一些替代方式来解读分析或寻找答案。

1.每个司机第一次和第二次违章相隔多长时间（以天数为单位）？

1. 为了在 Tableau Desktop 中回答此问题，我们将使用 DATEDIFF 函数。此函数采用三个参数 — 日期部分、开始日期和结束日期。由于我们想知道这些事件的间隔天数，因此将使用日期部分“day”。我们的开始日期和结束日期在数据集中为“**1st Infraction Date**”（第一次违章日期）和“**2nd Infraction Date**”（第二次违章日期）。
2. 计算为：
    
    **Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction Date], [2nd Infraction Date])`
3. 我们依据“**Driver ID**”（司机 ID）将该计算绘制为条形图。请注意，7 名司机没有第二次违章，因此有 7 个 null 值。
    
    ![按违章间隔时间列出的司机条形图](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_1desktopI.png)

2.比较第一次和第二次违章的罚款金额。这些金额是否相关？

1. 为了在 Tableau Desktop 中回答此问题，我们将创建“**1st Fine Amount**”（第一次罚款金额）和“**2nd Fine Amount**”（第二次罚款金额）的散点图。通过将“**Driver ID**”（司机 ID）放到“标记”卡上的“详细信息”功能区，我们可以为每名司机创建一个标记。
2. 若要添加趋势线，请使用左侧窗格中的“分析”选项卡，并显示线性趋势线。将光标悬停在趋势线上，我们可以看到 R 平方值实际上为零，并且 p 值明显高于任何临界值。我们可以确定，第一次罚款金额和第二次罚款金额之间没有相关性。
    
    如果我们要在仪表板中使用此散点图，则应移除趋势线。

![按司机 ID 显示的第 1 次和第 2 次罚款金额的散点图，显示趋势线工具提示](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_1desktopII.png)

3.总体而言，哪个司机支付的罚款最多？谁支付的最少？

如果想要更深入地进行分析，我们可能需要创建一些计算。

1. 为了在 Tableau Desktop 中回答此问题，我们需要将两次违章的罚款添加到单一字段中。由于某些司机可能没有第二次违章，我们需要使用零 null `ZN` 函数将“**2nd Fine Amount**”（第二次罚款金额）的任何 null 值转换为零。如果没有第二次罚款，不这样做将导致 null。
2. 计算为：
    
    **Total Amount Paid**（总支付金额）= `[1st Fine Amount] + ZN([2nd Fine Amount])`
3. 我们可以依据“**Driver ID**”（司机 ID）绘制“**Total Amount Paid**”（总支付金额）条形图并对其进行排序。

![按司机 ID 显示的已支付金额条形图，计算编辑器显示已支付金额](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_1desktopIII.png)

4.有多少司机有过多种类型的违章？

1. 为了在 Tableau Desktop 中回答此问题，我们需要执行一个更细致的 `IF` 计算，比较第一次和第二次违章类型是否相同。如果相同，我们想要分配值“1”。如果不同，我们将分配“2”。由于我们仅关心多个违章类型，因此任何其他结果（例如 null 第二次违章类型）将被分配“1”。
2. 计算为：
    
    **Number of Infraction Types**（违章类型数量）=
    
    IF \[1st Infraction Type\]=\[2nd Infraction Type\] THEN 1 
    ELSEIF \[1st Infraction Type\]!= \[2nd Infraction Type\] THEN 2 
    ELSE 1 END
3. 然后，我们可以依据“**Driver ID**”（司机 ID）绘制“**Number of Infraction Types**”（违章类型数量）条形图并对其进行排序。

![按司机 ID 显示的违章类型数量条形图，计算编辑器显示违章类型数量](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_1desktopIV.png)

5.从未上过交通法规学习班的司机的平均罚款金额是多少？

1. 为了在 Tableau Desktop 中回答此问题，我们无法简单地将总罚款金额除以二，因为某些司机只有一次违章。我们也无法计算每个司机的平均罚款并取这些值的平均值，因为对平均值进行平均计算可能会导致不一致。作为替代，我们需要计算从未上过交通法规学习班的司机支付的总金额，然后除以与这些罚款关联的总违章次数。
    
    1. 首先，我们需要确定是否每个司机都有第二次违章。如果没有第二次违章，则所有“2nd”字段中的信息都将为 null，我们可以利用这一事实并开始构建计算：
        
        `IFNULL([2nd Infraction Type], 'no')`
        
        这将返回违章类型（如果存在），或者如果没有第二次违章，则返回“no”。
    2. 接着，我们需要将此信息转换为违章次数 1 或 2。如果 `IFNULL` 计算的结果为“no”，则应将司机标记为有一次罚款。对于任何其他结果，则应标记为有两次罚款。计算为：
        
        **Number of Infractions**（违章次数）=
        
        IF IFNULL(\[2nd Infraction Type\], 'no') = 'no' THEN 1 
        ELSE 2 
        END
    3. 现在，我们需要考虑总罚款金额。与上面的问题 3 类似，我们将添加第一次和第二次罚款金额，并将第二次罚款金额放在 `ZN` 函数内。但是，由于我们想在整个数据集级别计算此值，因此最佳做法是在计算本身中指定聚合 SUM。计算为： 
        
        `SUM([1st Fine Amount]) + SUM( ZN([2nd Fine Amount]) )`
    4. 总而言之，我们将利用这个总罚款金额，并将其除以新的“**Number of Infractions**”（违章次数）计算字段，从而确定平均罚款金额：
        
        **Average Fine**（平均罚款）= `( SUM([1st Fine Amount]) + SUM( ZN([2nd Fine Amount]) ) ) / SUM([Number of Infractions])`
2. 我们还需要筛选出曾经参加过交通法规学习班的司机 — 但该信息也跨两个字段存储。
    
    1. Tableau 在进行数值计算时非常高效。我们将用尽可能多可帮助提高性能的数字表述这一点。为了合并这两个字段，我们将为每个字段创建一个显示为“Yes = 1”和“No = 0”的字段（对于没有第二次违章的司机，null 也应 = 0）。通过对这些计算的结果求和，总值为 0 的任何司机从未上过交通法规学习班（值 1 或 2 表示他们上过交通法规学习班的次数）。我们随后可以进行筛选，以仅保留值为 0 的司机。
    2. 这一次，我们将使用 `CASE` 语句，而不是 `IF`。这些语句的功能非常相似，但具有不同的语法。计算的开头应如下所示：
        
        CASE \[1st Traffic School\]
        WHEN 'Yes' THEN 1
        WHEN 'No' THEN 
        ELSE 0
        END
    3. 然后，我们将为“2nd Traffic School”执行相同的操作。通过将每个 CASE 语句放在括号内并在两者之间添加一个加号，我们可以在同一计算中添加这两段语句。移除一些换行符，它看起来如下所示：
        
        **Number of Traffic School Attendances**（参加交通法规学习班次数）=
        
        (CASE \[1st Traffic School\] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END)
        +
        (CASE \[2nd Traffic School\] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END)
    4. 如果我们将“**Number of Traffic School Attendances**”（参加交通法规学习班次数）拖到“数据”窗格的“维度”区域，则值 0–2 将变为离散。
    5. 现在，如果我们对“**Number of Traffic School Attendances**”（参加交通法规学习班次数）进行筛选，我们可以只选择 0，并且知道我们获得了从未参加过交通法规学习班的司机。
3. 为了回答原来的问题，我们只需将“**Average Fine**”（平均罚款）放到“标记”卡上的“文本”功能区。
    
    由于我们在计算中构建了聚合，因此胶囊上的聚合将为 AGG，我们无法对其进行更改。这是预期行为。

![打开计算编辑器以显示交通法规学习班出勤人数的平均支付金额的单个数字视图](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_1desktopV.png)

## 更进一步 — 转置的数据

尽管我们一直在处理的数据结构良好，可以解决与第一次和第二次违章特定相关的问题，但它并不是我们建议用于 Tableau Desktop 的标准结构。我们的分析背离与违章日期相关的基本问题越远，用于将相关信息合并为可用形式的计算就会变得越复杂。

通常，如果为同一类型的数据使用多个列存储数据（例如两个列用于日期，两个列用于罚款金额等），并且唯一的信息存储在字段名中（例如是第一次还是第二次违章），这就表明应该要对数据进行转置。

在 Tableau Prep Builder 中执行多重转置可以很好地处理这一点。我们可以从上一教程[使用 Tableau Prep 查找第二个日期](https://help.tableau.com/v0.0/prep/zh-cn/prep_tutorial_2nddateA.htm)中创建的“**Driver Infraction”**（司机违章）Tableau Prep 流程的末尾开始工作。

> [!TIP]
> 
> **提示**：请确保返回 Tableau Prep 来执行这些步骤。

1. 从最后的清理步骤中，添加一个“Pivot”（转置）步骤，该步骤将按每个重复的字段进行转置。使用“转置的字段”区域右上角的加号 ![带有加号的按钮。](/tableau/next/zh-cn/prep/zh-cn/Img/prep_addicon.png) 图标来添加更多“转置值”。每一组字段（例如第一次和第二次罚款金额）应一起转置。
    
    有关转置的详细信息，请参见[清理和调整数据](https://help.tableau.com/v0.0/prep/zh-cn/prep_clean.htm#Pivot)。
2. 在“转置的字段”区域中的“Pivot1 Names”（转置 1 名称）列下，双击每个值并将其重命名为“1st”（第一）和“2nd”（第二）。
    
    ![Tableau Prep 中的“Pivot 1”（转置 1）配置窗格，显示五个转置](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_2desktop.png)

通过移除 null 日期以及重命名字段并对其进行重新排序，可以对结果进行整理。

1. 在转置后面添加清理步骤。在“**Infraction Date**”（违章日期）列中，右键单击 null 栏并选择“排除”。
2. 双击字段名“**Pivot1 Names**”（转置 1 名称），并将其重命名“**Infraction Number**”（违章次数）。
3. 视情况而定拖动数据以按如下方式对其进行重新排序：
    
    ![转置和清理的数据的预览](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_2desktopdata.png)

1. 依据新的转置的数据创建一个名为“**Pivoted Driver Infractions**”（转置的司机违章）的输出，并将其引入 Tableau Desktop。（在添加“输出”步骤之后，不要忘记运行流程。）

现在，我们可以用这个转置的数据结构再次探讨我们的五个问题；如果遇到困难，您可以展开每个问题来了解有关如何继续操作的基本信息。

**注意：**您可以下载完成的流程文件 [Pivoted Driver Infractions.tflx](https://mkt.tableau.com/PrepTutorials/Pivoted%20Driver%20Infractions.tflx) 来检查您的工作，或者下载工作簿 [Pivoted Driver Infractions.twbx](https://mkt.tableau.com/PrepTutorials/Pivoted%20Driver%20Infractions.twbx) 在上下文中查看解决方案。请记住，可以通过一些替代方式来解读分析或寻找答案。

1.每个司机第一次和第二次违章相隔多长时间（以天数为单位）？

1. 为了在 Tableau Desktop 中回答此问题，就像我们对第一个数据集所做的那样，我们将使用 `DATEDIFF` 函数。此函数需要一个开始日期和一个结束日期。此信息存在于我们的数据中，但全部在一个字段中。我们需要将其拉出到两个字段中。
    
    1. 创建两个初始计算字段：
        
        **1st Infraction Date**（第一次违章日期）= `IF [Infraction Number] = "1st" THEN [Infraction Date] END`
        
        **2nd Infraction Date**（第二次违章日期）= `IF [Infraction Number] = "2nd" THEN [Infraction Date] END`
    2. 由于我们想要确保这两个字段均可用于为每个司机比较，我们需要将它们固定到“**Driver ID**”（司机 ID）级别。
        
        **注意：**不相信我？尝试按原样对这两个字段进行 `DATEDIFF` 计算：**Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction Date], [2nd Infraction Date])`  
        您将在所有地方都获得 null 结果，因为 Tableau 会尝试跨类似于如下的数据结构进行比较：  
        ![显示 null 结果的文本表](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_2desktopnulls.png)  
        在这里，知道第一个日期的行不知道第二个日期，反之亦然。为了解决此问题，我们将使用“FIXED”详细级别表达式将第一个和第二个日期强制与“**Driver ID**”（司机 ID）相关。
    
    按如下方式编辑计算：
    
    **1st Infraction Date**（第一次违章日期）= `{ FIXED [Driver ID] : MIN ( IF [Infraction Number] = "1st" THEN [Infraction Date] END ) }`
    
    **2nd Infraction Date**（第二次违章日期）= `{ FIXED [Driver ID] : MIN ( IF [Infraction Number] = "2nd" THEN [Infraction Date] END ) }`
    
    **注意：**如果嵌入在 LOD 表达式中，必须对原始 `IF` 计算进行聚合。我们可以使用任何将保留日期值的基本聚合（因此像 SUM、AVG 或 MIN 这样的聚合有用，而 CNT 或 CNTD 则不行）。
    
    **注意**：这些计算也可以在 Tableau Prep Builder 中创建。有关 Prep 中 LOD 表达式的详细信息，请参见[创建详细级别、排名和分片计算](https://help.tableau.com/v0.0/prep/zh-cn/prep_calculations.htm)。
    
    8. 现在，我们可以按如下方式创建 `DATEDIFF` 计算：
        
        **Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction Date], [2nd Infraction Date])`
        
        - 如果我们想要查看周或月，只需修改日期部分（当前为 `'day'`）。
        - 也可以通过将 FIXED 计算直接放在 DATEDIFF 中为整个事件创建单一计算： 
            
            DATEDIFF ( 'day', 
            
            { FIXED \[Driver ID\] : MIN ( IF \[Infraction Number\] = "1st" THEN \[Infraction Date\] END ) },
            
            { FIXED \[Driver ID\] : MIN ( IF \[Infraction Number\] = "2nd" THEN \[Infraction Date\] END ) }
            
            )
    9. 在“列”上绘制“**Time Between Infractions**”（违章间隔时间），并在“行”上绘制“**Driver ID**”（司机 ID）。

结果将与使用未转置数据结构的结果相同。

2.比较第一次和第二次违章的罚款金额。这些金额是否相关？

1. 为了在 Tableau Desktop 中回答此问题，我们将使用与上一个问题非常相似的逻辑。我们将使用“**Infraction Number**”（违章次数）来确定给定行是第一次违章还是第二次违章，然后相应拉出罚款金额。
    
    1. 如果我们只需要建立散点图，则可以跳过 LOD 部分，只使用 `IF` 计算：
        
        **1st Fine Amount**（第一次罚款金额）= `IF [Infraction Number] = "1st" THEN [Fine Amount] END`
        
        **2nd Fine Amount**（第二次罚款金额）= `IF [Infraction Number] = "2nd" THEN [Fine Amount] END`
    2. 但是，如果我们想要进行比较并查看某个司机第一次和第二次罚款之间的金额差异，我们会遇到像日期一样的 null 问题。将这些计算封装在 `FIXED` LOD 内不会有什么坏处，因此从一开始这样做或许会不错：
        
        **1st Fine Amount**（第一次罚款金额）= `{ FIXED [Driver ID] : MIN ( IF [Infraction Number] = "1st" THEN [Fine Amount] END ) }`
        
        **2nd Fine Amount**（第二次罚款金额）= `{ FIXED [Driver ID] : MIN ( IF [Infraction Number] = "2nd" THEN [Fine Amount] END ) }`
        
        这些计算也可以在 Tableau Prep Builder 中创建。有关 Prep 中 LOD 表达式的详细信息，请参见[创建详细级别、排名和分片计算](https://help.tableau.com/v0.0/prep/zh-cn/prep_calculations.htm)。
    3. 在“列”上创建包含“**1st Fine Amount**”（第一次罚款金额）的散点图，在“行”上创建包含“**2nd Fine Amount**”（第二次罚款金额）的散点图，并像以前一样显示线性趋势线。

结果将与使用未转置数据结构的结果相同。

3.总体而言，哪个司机支付的罚款最多？谁支付的最少？

1. 为了在 Tableau Desktop 中回答此问题，转置的数据结构非常理想。我们只需在条形图中显示“**Driver ID**”（司机 ID）和“**Fine Amount**”（罚款金额）。默认聚合已经是 SUM，因此将自动绘制司机支付的总金额。

结果将与使用未转置数据结构的结果相同。

4.有多少司机有过多种类型的违章？

1. 为了在 Tableau Desktop 中回答此问题，转置的数据结构非常理想。我们只需以条形图形式显示“**Driver ID**”（司机 ID）以及“**Infraction Type**”（违章类型）的“不重复计数”，就能得到答案。

结果将与使用未转置数据结构的结果相同。

5.从未上过交通法规学习班的司机的平均罚款金额是多少？

1. 为了在 Tableau Desktop 中回答此问题，我们无法简单地将总罚款金额除以二，因为某些司机只有一次违章。我们也无法计算每个司机的平均罚款并取这些值的平均值，因为对平均值进行平均计算可能会导致不一致。作为替代，我们需要计算从未上过交通法规学习班的司机支付的总金额，然后除以与这些罚款关联的总违章次数。
    
    1. 首先，我们需要确定是否每个司机都有第二次违章。如果没有第二次违章，则“**2nd Infraction Date**”（第二次违章日期）将为 null，我们可以利用这一事实并开始构建计算：
        
        `IFNULL(STR([2nd Infraction Date]), 'no')`
        
        这将返回第二次违章的日期（如果存在），或者如果没有第二次违章，则返回“no”。
        
        **注意：**此计算的 `STR` 部分是必需的，因为 `IFNULL` 需要在其参数中保持数据类型的一致性。由于我们想要为 null 值返回字符串“no”，因此也要将日期转换为字符串。
    2. 接着，我们需要将此信息转换为违章次数 1 或 2。如果 `IFNULL` 计算的结果为“no”，则应将司机标记为有一次罚款。对于任何其他结果，则应标记为有两次罚款。计算为：
        
        **Number of Infractions**（违章次数）=
        
        IF IFNULL(STR(\[2nd Infraction Date\]), 'no')= 'no' THEN 1 
        ELSE 2 
        END
    3. 现在，我们需要考虑平均罚款金额。我们已经有了一个“**Fine Amount**”（罚款金额）字段。我们只需将该字段除以新的“**Number of Infractions**”（违章次数）字段，同时将两者都放在 SUM 内： 
        
        **Average Fine**（平均罚款）= `(SUM([Fine Amount]) / SUM([Number of Infractions])`
2. 我们还需要筛选出参加过交通法规学习班的司机。看起来我们可以使用**“Traffic School”（交通法规学习班）**字段并依据**“Traffic School = no”**进行筛选。但是，这将依据未与交通法规学习班关联的_违章行为_（而不是从示参加过交通法规学习班_司机_）进行筛选。如果一名司机因一项违章行为（而不是另一项违章行为）而参加交通法规学习班，我们不希望在这里考虑任何违章行为 - 该司机已经参加过交通法规学习班，因此不符合问题的参数。
    
    我们需要做的是筛选出参加过交通法规学习班的任何司机。就数据而言，我们想要筛选出任何行上“**Traffic School**”（交通法规学习班）为“Yes”（是）的任何司机，而不管它与哪种违章行为关联。让我们分阶段构建计算，并使用一个简单的视图来帮助跟踪所发生的情况：
    
    1. 首先，我们想要知道司机的“Traffic School”（交通法规学习班）是否为“Yes”（是）。将“**Driver ID**”（司机 ID）拖到“行”，并将“**Traffic School**”（交通法规学习班）拖到“列”。我们将获得一个文本表，其中占位符“Abc”文本表示每个司机的相关值。
    2. 接着，我们想要构建一个计算，该计算将确定“**Traffic School**”（交通法规学习班）的值是否为“Yes”（是）。计算的第一个阶段是：
        
        **Attended Traffic School**（已参加交通法规学习班）= `CONTAINS([Traffic School), 'Yes')`
        
        如果我们将“**Attended Traffic School**”（已参加交通法规学习班）拖到“标记”卡上的“颜色”功能区，我们会看到它准确地针对“No”（否）列中的每个标记标为“False”，并针对“Yes”（是）列中的每个标记标为“True”。
        
        ![一个可视化项，对于“No”（否）列中的每个标记，带有“False”标签；对于“Yes”（是）列中的每个标记，带有“True”标签。](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_pivotedV2.png "按“Traffic School”（交通法规学习班）列出的“Driver ID”（司机 ID）的视图，“Attended Traffic School”（已参加交通法规学习班）放在“颜色”上")
    3. 但是，我们真正需要的是位于_司机_（而不是_违章_）级别的此信息。尝试在与基本数据结构不同的详细级别计算结果时，LOD 表达式非常适合。我们将此表达式设为 `FIXED` LOD 表达式。但是，正如我们所知，必须对 LOD 的聚合表达式进行聚合。以前，我们使用过 MIN。它在这里是否有用？我们将计算修改为： 
        
        **Attended Traffic School**（已参加交通法规学习班）= `{ FIXED [Driver ID] : MIN( CONTAINS([Traffic School], 'Yes'))}`
        
        ![按“Traffic School”（交通法规学习班）列出的“Driver ID”（司机 ID）的视图，“Attended Traffic School”（已参加交通法规学习班）放在“颜色”上](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_pivotedV3.png)
        
        在视图中应用该更改之后，我们看到了与预期相反的结果。具有“No”值的任何司机全部被标记为“False”。相反，我们希望为该司机的每条记录将“Yes”标记为“True”。MIN 在这里做了什么？它按字母顺序选取第一个响应，即“No”。
    4. 如果我们将其更改为 MAX，将会怎么样？该函数是否会按字母顺序获取最后一个响应？我们将计算修改为： 
        
        **Attended Traffic School**（已参加交通法规学习班）= `{ FIXED [Driver ID] : MAX ( CONTAINS( [Traffic School], 'Yes') ) }`
        
        ![按“Traffic School”（交通法规学习班）列出的“Driver ID”（司机 ID）的视图，“Attended Traffic School”（已参加交通法规学习班）放在“颜色”上](/tableau/next/zh-cn/prep/zh-cn/Img/prep_tutorial_2d_pivotedV4.png)
        
        我们得到了想要的结果：如果司机在数据中的任何位置有“Yes”， 他们会因为参加过交通法规学习班而被标记为“True”，即使对于不涉及交通法规学习班的违章也是如此。
    5. 如果我们将“**Attended Traffic School**”（已参加交通法规学习班）拖到“筛选器”功能区并仅选择“False”，将只会剩下从未参加过交通法规学习班的司机。
3. 为了回答原来的问题，在筛选器就位的情况下，我们只需将“**Average Fine**”（平均罚款）放到“标记”卡上的“文本”功能区。由于我们在计算中构建了聚合，因此字段上的聚合将为 AGG，我们无法对其进行更改。这是预期行为。

结果将与使用未转置数据结构的结果相同。

### 转置数据的优点

如果知道我们只需要回答可以使用教程中的原始数据结构轻松回答的问题，我们可以继续使用该结构。不过，转置的数据格式更加灵活。即使它需要一些计算，但在计算就位之后，生成的数据集将非常适合于回答更广泛的问题。

## 再进一步 — 只使用计算

如果您无法访问 Tableau Prep Builder，将会怎么样？如果被迫使用原始数据，您是否完全不走运？根本不会！

Tableau Desktop 和 LOD 表达式可以回答所有分析问题。如果我们连接到原始的 [**Traffic Violations.xlsx**](https://mkt.tableau.com/PrepTutorials/Traffic%20Violations.xlsx)，它看起与已转置数据集非常相似 — 只是没有重要的“**Infraction Number**”（违章次数）字段。我们将需要通过 LOD 表达式模拟聚合步骤的输出。

**注意：**您可以下载工作簿 [LOD Driver Infractions.twbx](https://mkt.tableau.com/PrepTutorials/LOD%20Driver%20Infractions.twbx)，在上下文中探讨解决方案。请记住，可以通过一些替代方式来解读分析或寻找答案。

1.每个司机第一次和第二次违章相隔多长时间（以天数为单位）？

1. 为了在 Tableau Desktop 中回答此问题，我们将再次使用 `DATEDIFF` 函数。此函数需要一个开始日期和一个结束日期。此信息存在于我们的数据中，但全部在一个字段中。我们需要将其拉出到两个字段中。由于我们想要确保这两个字段均可用于为每个司机比较，我们需要将它们固定到“**Driver ID**”（司机 ID）级别。
    
    1. 为了查找第一次违章日期，我们将使用计算：
        
        **1st Infraction**（第一次违章）= `{ FIXED [Driver ID] : MIN ( [Infraction Date] ) }`
    2. 我们将分阶段执行第二次违章日期相关操作。
        
        1. 首先，我们需要只查看比第一个日期大的日期：
            
            `IF [Infraction Date] > [1st Infraction] THEN [Infraction Date] END`
        2. 但这将列出第一次违章之后_每次_违章，而我们只需要第二次违章。因此我们需要这些日期中最小的日期。将整个计算放在 MIN 内：
            
            `MIN( IF [Infraction] : [1st Infraction] THEN [Infraction Date] END )`
        3. 我们还想要为每个司机重新计算第二次违章日期。这就是 LOD 表达式的用武之地。我们会将此计算固定到“**Driver ID**”（司机 ID）级别：
            
            **2nd Infraction**（第二次违章）= `{ FIXED [Driver ID] : MIN ( IF [Infraction Date] > [1st Infraction] THEN [Infraction Date] END ) }`
    3. 现在我们可以创建 `DATEDIFF` 计算：
        
        **Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction], [2nd Infraction])`

结果将与使用其他两个数据结构的结果相同。

2.比较第一次和第二次违章的罚款金额。这些金额是否相关？

1. 为了在 Tableau Desktop 中回答此问题，我们将使用此问题的转置数据版本相似的逻辑。我们将使用为问题 I 创建的“**1st Infraction**”（第一次违章）和“**2nd Infraction**”（第二次违章）来确定给定行是第一次违章还是第二次违章，然后相应拉出罚款金额。
    
    1. 如果我们只需要建立散点图，则可以跳过 LOD 部分，只使用 `IF` 计算：
        
        **1st Fine Amount**（第一次罚款金额）= `IF [1st Infraction] = [Infraction Date] THEN [Fine Amount] END`
        
        **2nd Fine Amount**（第二次罚款金额）= `IF [2nd Infraction] = [Infraction Date] THEN [Fine Amount] END`
    2. 但是，如果我们想要进行比较并查看某个司机第一次和第二次罚款之间的金额差异，我们会遇到像第一个数据结构中一样的 null 问题。将这些计算封装在 FIXED LOD 内不会有什么坏处，因此从一开始这样做或许会不错：
        
        **1st Fine Amount**（第一次罚款金额）= ``{ FIXED [Driver ID] : MIN ( `IF [1st Infraction] = [Infraction Date] THEN [Fine Amount] END` ) }``
        
        **2nd Fine Amount**（第二次罚款金额）= ``{FIXED [Driver ID] : MIN( `IF [2ndInfraction] = [Infraction Date] THEN [Fine Amount] END` ) }``

结果将与使用其他两个数据结构的结果相同。

3.总体而言，哪个司机支付的罚款最多？谁支付的最少？

1. 为了在 Tableau Desktop 中回答此问题，我们需要了解有关仅 LOD 方法的一些信息。使用 Tableau Prep 的两种方法都会为司机筛选出不是第一次或第二次违章的记录。Tableau Desktop 中的 LOD 方法会保留所有记录。这意味着，如果我们按“**Driver ID**”（司机 ID）创建“**SUM(Amount Paid)**”的可视化项，仅 Tableau Desktop 版本将为违章超过两次的司机显示更高的金额。若要从与其他方法匹配的完整数据中获取“**Total Amount Paid**”（总支付金额）值，而不是使用原始“**Fine Amount**”（罚款金额）字段，则我们需要像使用第一个数据结构所做的那样将第一次和第二次罚款加总。
2. 使用我们为问题 2 创建的字段，我们将添加两个罚款金额。`ZN` 是必要的，以防止任何只有一次违章的司机的结果为 null。计算为： 
    
    **Total Amount Paid**（总支付金额）= `[1st Fine Amount] + ZN([2nd Fine Amount])`

结果将与使用其他两个数据结构的结果相同。

4.有多少司机有过多种类型的违章？

1. 为了在 Tableau Desktop 中回答此问题，我们不能简单地显示“**Driver ID**”（司机 ID）和“**Infraction Type**”（违章类型）的不重复计数。由于此数据集有超过第二次的违章，因此某些司机可能有两种以上的违章类型。为了使结果与其他方法相符，我们需要将范围限制为仅包含前两次违章。

1. 我们可以拉出第一次和第二次违章类型，将这些类型放在 LOD 表达式中，使它们 `FIXED` （固定）到司机级别，然后使用 `IF` 计算来对类型计数：
    
    1. **1st Infraction Type**（第一次违章类型）= `{ FIXED [Driver ID] : MIN ( IF [1st Infraction] = [Infraction Date] THEN [Infraction Type] END ) }`
    2. **2nd Infraction Type**（第二次违章类型）= `{ FIXED [Driver ID] : MIN ( IF [2nd Infraction] = [Infraction Date] THEN [Infraction Type] END ) }`
    3. **Number of Infraction Types**（违章类型数量）=
        
        IF \[1st Infraction Type\] = \[2nd Infraction Type\] THEN 1 
        ELSEIF \[1st Infraction Type\] != \[2nd Infraction Type\] THEN 2
        ELSE 1 END 
        
        **注意：**也可以通过直接将初始计算嵌入在较大的计算中，以单一字段的形式创建多个这样的计算。此处的合并计算将如下所示：  
        `IF   {FIXED [Driver ID] : MIN(IF [1st Infraction]=[Infraction Date] THEN [Infraction Type] END)}   =   {FIXED [Driver ID] : MIN(IF [2nd Infraction]=[Infraction Date] THEN [Infraction Type] END)}   THEN 1      ELSEIF   {FIXED [Driver ID] : MIN(IF [1st Infraction]=[Infraction Date] THEN [Infraction Type] END)}   !=   {FIXED [Driver ID] : MIN(IF [2nd Infraction]=[Infraction Date] THEN [Infraction Type] END)}   THEN 2      ELSE 1   END`  
          
        理解起来有点困难，但如果愿意使用的话，它确实有用。（请注意，换行符和一些空格不会影响 Tableau 解读计算的方式。）
2. 然后，我们可以依据“**Driver ID**”（司机 ID）绘制“**Number of Infraction Types**”（违章类型数量）条形图并对其进行排序。

结果将与使用其他两个数据结构的结果相同。

5.从未上过交通法规学习班的司机的平均罚款金额是多少？

1. 为了在 Tableau Desktop 中回答此问题，我们无法简单地将总罚款金额除以二，因为某些司机只有一次违章。我们也无法计算每个司机的平均罚款并取这些值的平均值，因为对平均值进行平均计算可能会导致不一致。作为替代，我们需要计算从未上过交通法规学习班的司机支付的总金额，然后除以与这些罚款关联的总违章次数。
    
    1. 首先，我们需要确定是否每个司机都有第二次违章。如果没有第二次违章，则所有“2nd”字段中的信息都将为 null，我们可以利用这一事实并开始构建计算：
        
        `IFNULL([2nd Infraction Type], 'no')`
        
        这将返回违章类型（如果存在），或者如果没有第二次违章，则返回“no”。
    2. 接着，我们需要将此信息转换为违章次数 1 或 2。如果 `IFNULL` 计算的结果为“no”，则应将司机标记为有一次罚款。对于任何其他结果，则应标记为有两次罚款。计算为：
        
        **Number of Infractions**（违章次数）=
        
        IF IFNULL(\[2nd Infraction Type\], 'no') = 'no' THEN 1
        ELSE 2
        END
    3. 对于“Total Amount Paid”（总支付金额），我们可以使用问题 3 中的计算。总而言之，我们将利用这个总罚款金额，并将其除以新的“**Number of Infractions**”（违章次数）计算字段，从而确定平均罚款金额：
        
        **Average Fine**（平均罚款）= `SUM([Total Amount Paid]) / SUM([Number of Infractions])`
2. 我们还需要筛选出参加过交通法规学习班的司机。由于此数据集包含一些有第三次或第四次违章的司机，因此我们无法使用与转置数据结构相同的方法。相反，我们将采用与未转置数据相同的方法，总结如下：
    
    1. 首先，我们需要构建两个计算，确定第一次和第二次违章是否涉及交通法规学习班： 
        
        **1st Traffic School**（第一次交通法规学习班）= `{ FIXED [Driver ID] : MIN (IF [1st Infraction] = [Infraction Date] THEN [Traffic School] END ) }`
        
        **2nd Traffic School**（第二次交通法规学习班）= `{ FIXED [Driver ID] : MIN (IF [2nd Infraction] = [Infraction Date] THEN [Traffic School] END ) }`
    2. 然后，我们将添加这些值来获取参加交通法规学习班的总次数： 
        
        **Number of Traffic School Attendances**（参加交通法规学习班次数）=
        
        (CASE \[1st Traffic School\] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END)
        +
        (CASE \[2nd Traffic School\] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END)
    3. 如果我们将“**Number of Traffic School Attendances**”（参加交通法规学习班次数）拖到“数据”窗格的“维度”区域，则值 0–2 将变为离散。
    4. 现在，如果我们对“**Number of Traffic School Attendances**”（参加交通法规学习班次数）进行筛选，我们可以只选择 0，并且知道我们获得了从未参加过交通法规学习班的司机。
3. 为了回答原来的问题，我们只需将“**Average Fine**”（平均罚款）放到“标记”卡上的“文本”功能区。由于我们在计算中构建了聚合，因此字段上的聚合将为 AGG，我们无法对其进行更改。这是预期行为。

结果将与使用其他两个数据结构的结果相同。

请务必记住，此解决方案包含大量的嵌套计算和 LOD 表达式。视数据集的大小以及数据集的复杂性而定，可能会出现性能问题。

## 对各种方法的反思

那么您应该采用哪种方法呢？这完全取决于您，并且工具任由您随意使用。

- 如果您想要避开 LOD，则可以使用数据调整解决方案，尽管对于某些分析（[Tableau Desktop 中的分析](#Desktop)）计算可能是必需的。
- 如果您能够调整数据并且熟悉计算（包括 LOD），则中间型选项可以提供最好的灵活性（[更进一步 — 转置的数据](#Further)）。
- 如果您熟悉 LOD、对性能的影响较小，并且/或者您无法访问 Tableau Prep，则单独使用 LOD 解决此问题是可行的选项（[再进一步 — 只使用计算](#Furtherer)）。

至少，它对于了解 Tableau Prep 中的聚合和 Tableau Desktop 中的详细级别表达式如何相互关联以及会对数据分析产生怎样的影响是有价值的。如同 Tableau 中的大多数功能一样，执行任何操作的方法都有多种。探索各种各样的选项可以帮助将概念联系在一起，让您能挑选最适合于自己的解决方案。

使用的计算：

### Driver Infractions（司机违章）

- **Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction Date], [2nd Infraction Date])`
- **Total Amount Paid**（总支付金额）=`[1st Fine Amount] + ZN([2nd Fine Amount])`
- **Number of Infraction Types**（违章类型数量）= `IF [1st Infraction Type]=[2nd Infraction Type] THEN 1 ELSEIF [1st Infraction Type]!= [2nd Infraction Type] THEN 2 ELSE 1 END`
- **Number of Infractions**（违章次数）= `IF IFNULL([2nd Infraction Type], 'no') = 'no' THEN 1 ELSE 2 END`
- **Average Fine**（平均罚款）= `( SUM([1st Fine Amount]) + SUM( ZN([2nd Fine Amount]) ) ) / SUM([Number of Infractions])`
- **Number of Traffic School Attendances**（参加交通法规学习班次数）= `(CASE [1st Traffic School] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END) + (CASE [2nd Traffic School] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END)`

### Pivoted Driver Infractions（转置的司机违章）

- **1st Infraction**（第一次违章）= `{FIXED [Driver ID] : MIN(IF [Infraction Number] = "1st" THEN [Infraction Date] END)}`
- **2nd Infraction**（第二次违章）= `{FIXED [Driver ID] : MIN(IF [Infraction Number] = "2nd" THEN [Infraction Date] END)}`
- **Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction], [2nd Infraction])`
- **1st Fine Amount**（第一次罚款金额）= `{FIXED [Driver ID] : MIN( IF [Infraction Number] = "1st" THEN [Fine Amount] END ) }`
- **Number of Infractions**（违章次数）= `IF IFNULL(STR([2nd Infraction]), 'no')= 'no' THEN 1 ELSE 2 END`
- **Average Fine**（平均罚款）= `SUM([Fine Amount])/SUM([Number of Infractions])`
- **Attended Traffic School**（已参加交通法规学习班）= `{ FIXED [Driver ID] : MAX( CONTAINS([Traffic School], 'Yes'))}`

### LOD Driver Infractions（LOD 司机违章）

- **1st Infraction**（第一次违章）= `{FIXED [Driver ID] : MIN([Infraction Date])}`
- **2nd Infraction**（第二次违章）= `{ FIXED [Driver ID] : MIN( IF [Infraction Date] > [1st Infraction] THEN [Infraction Date] END ) }`
- **Time Between Infractions**（违章间隔时间）= `DATEDIFF('day', [1st Infraction], [2nd Infraction])`
- **1st Fine Amount**（第一次罚款金额）= `{FIXED [Driver ID] : MIN( IF [1st Infraction] = [Infraction Date] THEN [Fine Amount] END ) }`
- **2nd Fine Amount**（第二次罚款金额）= `{FIXED [Driver ID] : MIN( IF [2nd Infraction] = [Infraction Date] THEN [Fine Amount] END ) }`
- **Total Amount Paid**（总支付金额）= `[1st Fine Amount] + ZN([2nd Fine Amount])`
- **1st Infraction Type**（第一次违章类型）= `{FIXED [Driver ID] : MIN( IF [1st Infraction] = [Infraction Date] THEN [Infraction Type] END ) }`
- **2nd Infraction Type**（第二次违章类型）= `{FIXED [Driver ID] : MIN( IF [2nd Infraction] = [Infraction Date] THEN [Infraction Type] END ) }`
- **Number of Infraction Types**（违章类型数量）= `IF [1st Infraction Type]=[2nd Infraction Type] THEN 1 ELSEIF [1st Infraction Type]!= [2nd Infraction Type] THEN 2 ELSE 1 END`
- **Number of Infractions**（违章次数）= `IF IFNULL([2nd Infraction Type], 'no') = 'no' THEN 1 ELSE 2 END`
- **Average Fine**（平均罚款）= `SUM ([Total Amount Paid]) / SUM([Number of Infractions])`
- **1st Traffic School**（第一次交通法规学习班）= `{FIXED [Driver ID] : MIN (IF [1st Infraction] = [Infraction Date] THEN [Traffic School] END ) }`
- **2nd Traffic School**（第二次交通法规学习班）= `{FIXED [Driver ID] : MIN (IF [2nd Infraction] = [Infraction Date] THEN [Traffic School] END ) }`
- **Number of Traffic School Attendances**（参加交通法规学习班次数）= `(CASE [1st Traffic School] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END) + (CASE [2nd Traffic School] WHEN 'Yes' THEN 1 WHEN 'No' THEN 0 ELSE 0 END)`

**注意：**特别感谢 Ann Jackson 的 Workout Wednesday 主题 [Do Customers Spend More on Their First or Second Purchase?（客户在第一次或第二次购买时是否花费更多？）](https://www.workout-wednesday.com/2018/06/13/week-24-do-customers-spend-more-on-their-first-or-second-purchase/)以及 Andy Kriebel 的 Tableau Prep 技巧 [Returning the First and Second Purchase Dates（返回第一次和第二次购买日期）](https://www.vizwiz.com/2018/06/first-second-purchase.html?m=1)，这些文章为本教程提供了最初的灵感。单击这些链接会使您离开 Tableau 网站。Tableau 对外部提供商所维护的页面的准确性或新鲜度不担负任何责任。如果您对其内容有任何疑问，请与所有者联系。