我们知道Power Query能够连接很多不同格式的数据源,但同时还有很多是不能直接支持的,比如word文档、压缩包等等。
有这样一种场景:源数据来自于网络或者远程服务器,每天更新,文件格式是zip,压缩包内有csv。
如果不能直接读取zip,那么我们需要每天把zip下载下来,解压后再导入Power Query,非常麻烦,有什么办法可以解决这个问题呢?
方法一:转换二进制数据流
对于电脑来说,任何文件都是二进制的数字,而M语言中提供了大量的binary类函数,所以我们可以利用函数来间接实现不解压直接读取zip压缩包。
代码来源于Mark White,涉及非常复杂的二进制数据流知识,我也看不懂。所以我稍微改了一下,封装成unzip的自定义函数,新建一个名为unzip的空查询,把下面代码复制粘贴到高级编辑器,需要用的时候直接调用自定义函数=unzip("C:\路径.zip")就行了。
(path) =>
let
Header = BinaryFormat.Record([
MiscHeader = BinaryFormat.Binary(14),
BinarySize = BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32, ByteOrder.LittleEndian),
FileSize = BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32, ByteOrder.LittleEndian),
FileNameLen= BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16, ByteOrder.LittleEndian),
ExtrasLen = BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16, ByteOrder.LittleEndian)
]),
HeaderChoice = BinaryFormat.Choice(
BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32, ByteOrder.LittleEndian),
each if _ <> 67324752
then BinaryFormat.Record([IsValid = false, Filename=null, Content=null])
else BinaryFormat.Choice(
BinaryFormat.Binary(26),
each BinaryFormat.Record([
IsValid = true,
Filename = BinaryFormat.Text(Header(_)[FileNameLen]),
Extras = BinaryFormat.Text(Header(_)[ExtrasLen]),
Content = BinaryFormat.Transform(
BinaryFormat.Binary(Header(_)[BinarySize]),
(x) => try Binary.Buffer(Binary.Decompress(x, Compression.Deflate)) otherwise null
)
]),
type binary
)
),
ZipFormat = BinaryFormat.List(HeaderChoice, each _[IsValid] = true),
Entries = List.Transform(
List.RemoveLastN( ZipFormat(File.Contents(path)), 1),
(e) => [FileName = e[Filename], Content = e[Content] ]
)
in
Table.FromRecords(Entries)
效果如下图,测试文件中包含了xls,xlsx,csv三个文件,三种格式都可以支持,解析出来还是binary,然后再根据格式使用Excel.Workbook或Csv.Document解析成表即可:
不仅是zip,其他的文件如doc,ppt,pdf等,理论上也都可以通过二进制读出来。
方法二:在PBID中使用R语言
可以看到用Power Query的M语言,因为没有直接的函数支持,导致虽然能实现效果,但过程实在太复杂,反正我是看不懂。
而在Python和R中一两行代码就可以搞定,所以只要是R可以,就可以在Power BI Desktop中使用R.Execute来调用R。
首先得安装R,这个就不多说了。假设在桌面上有个test.zip,压缩包内有个test.csv,我们需要读取csv的数据。源数据长这样:
先来一个不依赖包的写法:
R语言代码:
unzip("C:/Users/rages/Desktop/test.zip")
read.csv("test.csv",header=F)
header=F表示不使用第一行作为标题,header=T则相反。
把R语言写到PBID的高级编辑器中就有两个地方需要注意,一个是文本引号的转义,另一个是数据需要以dataframe数据框的形式返回。
let
unzip=R.Execute("
unzip(""C:/Users/rages/Desktop/test.zip"")
data
"){0}[Value]
in
unzip
在R中还有一个叫rio的包,可以不解压直接读取压缩包,首次使用需要install.packages('rio')安装rio包。
R语言中代码为:
library(rio)
import("c:/Users/rages/Desktop/test.zip")
代码很简洁,第一步加载rio包,第二步直接读取文件路径。
改写到PBID中为:
let
unzip=R.Execute("
library(rio)
data
"){0}[Value]
in
unzip