Power BI数据清洗避坑指南:从Power Query菜单到M语言代码的完整心法

当你第一次打开Power Query编辑器,面对密密麻麻的菜单按钮和陌生的M语言代码,是否感到无从下手?数据清洗作为数据分析的基石,直接决定了后续建模和可视化的质量。本文将带你避开常见陷阱,建立从界面操作到代码原理的完整知识体系。

1. 脏数据识别与初步处理

拿到一份销售订单与客户信息混合的Excel表格时,首先需要培养"数据侦探"的敏锐度。常见的脏数据特征包括:

  • 不一致的格式 :日期列中混用"2023/01/01"和"01-Jan-2023"
  • 缺失值陷阱 :空白单元格可能显示为null、NA或直接留空
  • 异常值干扰 :销售额列中出现负值或明显超出合理范围的数值
  • 重复记录 :同一订单ID出现多次但细节信息不一致

第一步总是先创建备份 :在Power Query编辑器中右键点击查询→选择"复制",保留原始数据副本。我曾见过同事因直接修改原始查询导致整个项目需要重做。

数据类型转换是新手最容易犯错的地方。点击列标题左侧的 ABC 图标时,系统会尝试自动判断类型,但经常出错。更可靠的做法是:

= Table.TransformColumnTypes(源, {{"订单日期", type date}})

注意:转换失败的值会变成红色错误提示,此时应检查原始数据而非强制转换

2. Power Query界面操作的黄金法则

2.1 菜单功能的战略选择

"主页"选项卡中的 删除行 功能有多个子选项,90%的情况只需要使用 删除空行 删除重复项 。我曾花费两小时调试一个公式,最后发现只是需要先执行 删除空行

"转换"选项卡中的 拆分列 功能看似简单,但选择 按分隔符 还是 按字符数 会导致完全不同的结果。对于地址字段,建议先用:

= Table.SplitColumn(源, "地址", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), {"地址.1", "地址.2"})

关键原则 :每次操作后立即在右侧"查询设置"窗格中为步骤命名(如"删除测试订单"),三个月后你会感谢这个习惯。

2.2 添加列的三种智慧

  1. 条件列 :处理业务规则最安全的方式
    = Table.AddColumn(源, "客户等级", each if [销售额] > 10000 then "VIP" else "普通")
    
  2. 索引列 :看似简单却是解决合并问题的钥匙
  3. 自定义列 :灵活但需要M语言基础,新手建议先用界面操作生成代码再修改

3. 高级技巧与M语言原理

3.1 合并查询的隐藏成本

使用 合并查询 功能时,系统默认保留所有行(左外连接),这可能导致数据膨胀。实际业务中更常用的是:

= Table.NestedJoin(订单表, {"客户ID"}, 客户表, {"ID"}, "客户详情", JoinKind.Inner)

连接类型对照表:

JoinKind参数 SQL等效 适用场景
Inner INNER JOIN 只保留匹配记录
LeftOuter LEFT JOIN 保留左表全部
RightOuter RIGHT JOIN 保留右表全部
FullOuter FULL OUTER JOIN 保留所有记录

3.2 透视与逆透视的艺术

销售数据经常以交叉表形式存在(月份作为列标题),此时 逆透视 是必备技能。但要注意:

  1. 先选择需要保留的列(如产品ID)
  2. 然后Ctrl+点击要转换的月份列
  3. 右键选择 逆透视列

生成的M代码实际上执行了:

= Table.UnpivotOtherColumns(源, {"产品ID"}, "属性", "值")

4. 调试与性能优化

当查询运行缓慢时,检查"查询诊断"工具(视图→查询诊断)。常见性能瓶颈:

  • 过早的类型转换 :应在所有清洗完成后最后统一转换
  • 不必要的计算列 :能用 Table.AddColumn 就不用在DAX中创建计算列
  • 低效的合并方式 :小表合并到大表时使用 Table.Join 而非界面操作

高级编辑器中的每个步骤都对应一个变量,可以通过临时注释部分代码来隔离问题。例如:

let
    源 = Excel.CurrentWorkbook(){[Name="订单"]}[Content],
    // 删除测试 = Table.SelectRows(源, each [订单类型] <> "测试"),
    类型转换 = Table.TransformColumnTypes(删除测试, {...})
in
    类型转换

数据清洗从来不是一次性工作。建立可复用的函数库(如统一地址清洗函数)才是专业做法。在空白查询中输入:

= (inputText as text) => Text.Proper(Text.Trim(inputText))

将其保存为"格式化文本"函数,以后任何查询都可以调用这个标准化流程。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐