【實(shí)例】PowerQuery的“透視”(Pivot)

緣起

就像我很多年都沒搞懂Excel數(shù)據(jù)透視表一樣慎框,我對PowerQuery的透視和逆透視也是一直迷迷糊糊烁焙。直到今天遇到一個實(shí)際的例子重窟。

我更新windows10時,從微軟網(wǎng)站上下載了一個關(guān)于此更新的文件信息csv文件妒穴。一時興起,想拿這個文件來練練手摊崭。

文件鏈接見:http://pan.baidu.com/share/link?shareid=2260904732&uk=3579801784

文件分析

這是一個csv文件讼油,我用Excel打開之后,發(fā)現(xiàn)這個文件其實(shí)分為兩部分:一部分是很標(biāo)準(zhǔn)的表格形式:

第一部分的格式.PNG

這種格式很好處理呢簸。

從第32544行開始矮台,數(shù)據(jù)變成另一種格式:

第32544行開始的數(shù)據(jù).PNG

我準(zhǔn)備建立三個查詢:一個是原始數(shù)據(jù),一個是第一部分?jǐn)?shù)據(jù)根时,再一個是第二部分?jǐn)?shù)據(jù)瘦赫。

原始數(shù)據(jù)處理思路

第一部分?jǐn)?shù)據(jù),從前三行都是無效數(shù)據(jù)蛤迎,因此可以通過Table.Skip()來跳過前三行确虱。
這里有個奇怪的地方是,我導(dǎo)入原始數(shù)據(jù)時替裆,用的是從csv文件導(dǎo)入校辩,但是PowerQuery居然不能正確識別分隔符,導(dǎo)致所有數(shù)據(jù)都在同一列辆童,我表示不知道為什么【2016年11月14日補(bǔ)充:應(yīng)該是PowerQuery老版本的bug之類的問題宜咒,我剛才將Office2016升級到1610.7466.2038后問題解決】。所以只好按文本處理把鉴,然后手動分列荧呐,最終代碼如下:

let
源 = Table.FromColumns({Lines.FromBinary(File.Contents("C:\Users\wangh\Downloads\3197954.csv"))}),
用分隔符分列 = Table.SplitColumn(源,"Column1",Splitter.SplitTextByDelimiter(","),{"Column1.1", "Column1.2", "Column1.3", "Column1.4", "Column1.5", "Column1.6", "Column1.7", "Column1.8", "Column1.9"}),
刪除的頂端行 = Table.Skip(用分隔符分列,3),
提升的標(biāo)題 = Table.PromoteHeaders(刪除的頂端行)
in
提升的標(biāo)題

第一部分?jǐn)?shù)據(jù)處理思路

第一部分?jǐn)?shù)據(jù)在原始數(shù)據(jù)查詢的基礎(chǔ)上進(jìn)行引用,這樣原始數(shù)據(jù)將來萬一需要任何修改的話纸镊,可以很方便地將修改結(jié)果反映到第一部分?jǐn)?shù)據(jù)處理這個查詢上倍阐。

這里的難點(diǎn)是如何安全高效剔除第二部分?jǐn)?shù)據(jù)。當(dāng)然可以加載到excel表格中逗威,手動找到第二部分開始的前一行的行號峰搪,然后回到查詢編輯器里,用Table.FirstN()來篩選出第一部分?jǐn)?shù)據(jù)凯旭。但這種辦法只適用于處理這一個文件概耻。PowerQuery的魅力在于流程化處理滿足條件的所有文件而不是某個特定文件,因此罐呼,需要用更動態(tài)的法子來獲取截斷處的行號鞠柄。我用的是:

List.PositionOf(Table.Column(源,"File name"),"Additional file information")

因?yàn)橥ㄟ^觀察發(fā)現(xiàn),截斷處附近包含文本"Additional file information"嫉柴⊙岫牛可以合理假設(shè)其他微軟的類似文件也都有這句話,所以找到這句話所在的行號,就可以截取了夯尽。

第一部分?jǐn)?shù)據(jù)處理的完整代碼如下:

let
源 = 原始,
保留的行 = Table.FirstN(源,List.PositionOf(Table.Column(源,"File name"),"Additional file information")),
已刪除的空行 = Table.SelectRows(保留的行, each not List.IsEmpty(List.RemoveMatchingItems(Record.FieldValues(_), {"", null})))
in
已刪除的空行

第二部分?jǐn)?shù)據(jù)處理思路

第二部分的數(shù)據(jù)有兩種方法可以獲得瞧壮,一是反向合并兩個表格,二是老老實(shí)實(shí)利用處理第一部分?jǐn)?shù)據(jù)的思路來獲取匙握。我采取的是第二種方法咆槽。

這里要注意的是,第二部分?jǐn)?shù)據(jù)開始的第一個數(shù)據(jù)片段圈纺,跟后面的數(shù)據(jù)片段相比秦忿,少了一行,所以蛾娶,這個片段是無效數(shù)據(jù)灯谣,需要剔除。我用了Table.Skip()來剔除茫叭。

接下來處理掉每個數(shù)據(jù)片段的空格。直接在UI上選擇刪除空行半等。

接下來就到了這個例子中最復(fù)雜的部分了揍愁。
數(shù)據(jù)都是每7行、每兩列為一條完整的記錄杀饵,因此莽囤,我們需要將六行兩列變成一行六列的有效記錄。

我們知道行變列可以用轉(zhuǎn)置的方法切距,但是在這個例子是行不通的朽缎,因?yàn)椴⒉皇呛唵蔚男辛修D(zhuǎn)換問題。

這里需要Table.Pivot()函數(shù)谜悟。

在開始透視之前话肖,還有一個額外的步驟要做,就是為每一條記錄設(shè)置一個ID葡幸。如果不設(shè)置這個ID最筒,直接透視,PowerQuery會報如下錯誤:

Expression.Error: 枚舉中用于完成該操作的元素過多蔚叨。

為什么會這樣床蜘,我還不清楚,我只知道獲取記錄的ID之后蔑水,就不會報錯邢锯。

獲取ID的第一步是為表格添加索引,可以直接在UI的“添加列”選項(xiàng)卡找到“添加索引列”命令搀别,我們添加一個開始為1的索引列丹擎。之所以開始為1,主要是我個人愛好——覺得這樣好理解歇父。其實(shí)在后面我們還要通過運(yùn)算將索引變?yōu)?開始才能正確獲取記錄的ID鸥鹉。

接下來是再添加一列蛮穿,獲取ID:

 ID = Table.AddColumn(已添加索引, "ID", each Number.IntegerDivide([索引]-1,6)),

可以看到將索引列減1就是將其變成從0開始。
這里這個Number.IntegerDivide()很過癮毁渗,就是返回兩個相除之后商的整數(shù)部分践磅。由于每一條記錄的六行所在的索引號減去1之后除以6,獲取的商的整數(shù)部分都相同灸异,因此府适,可以判斷出這里的整數(shù)商就是判斷是否同一條記錄的依據(jù),因此這是獲取ID的關(guān)鍵步驟肺樟。

注意獲取ID之后有一個重命名表格列名稱的步驟檐春,原因在于我們整理原始數(shù)據(jù)時將列標(biāo)題處理好了,但是現(xiàn)在第二部分?jǐn)?shù)據(jù)的列標(biāo)題需要從行里獲取么伯,如果獲取之后的列標(biāo)題和已有列標(biāo)題重復(fù)疟暖,PowerQuery會報錯。這個步驟可以在導(dǎo)入源之后的操作及Table.Pivot()操作中間的任何一步進(jìn)行田柔。

接下來是選取我們需要的列俐巴,刪除那些不需要的列。

到這一步硬爆,我們才真正開始用Table.Pivot()函數(shù)來獲取數(shù)據(jù):

已透視列 = Table.Pivot(刪除的其他列,List.Distinct(Table.Column(刪除的其他列,"Column1")), "Column1","Column2"),

我個人一直認(rèn)為這個Table.Pivot()函數(shù)很不好理解欣舵,事實(shí)上我也是折騰了好久才偶然弄出我想要的結(jié)果來。

第一個參數(shù)“刪除的列”是要透視的表格缀磕,這個沒什么好說缘圈。
第二個參數(shù)是要將行里的數(shù)據(jù)提取成為透視后的表格列標(biāo)題。在此例中袜蚕,Column1里邊的數(shù)據(jù)就是我們要變成列標(biāo)題的數(shù)據(jù)糟把。由于這一列的所有數(shù)據(jù)都是六個標(biāo)題行的重復(fù),因此我們需要獲取不重復(fù)值牲剃,因此

List.Distinct(Table.Column(刪除的其他列,"Column1"))

得到的其實(shí)是一個有6行的list糊饱。這個list的每一個元素將作為透視后的表格的每一列。


列標(biāo)題.PNG

【注意截圖中其實(shí)是7行颠黎。最后那一行x64 Windows10我表示很懵比柑晒,不知道它從哪里鉆出來的蒋情,后來檢查原始數(shù)據(jù)筹麸,才知這個第二部分的數(shù)據(jù)其實(shí)也是分了兩部分:x86和x64兩部分鹦马。鑒于我太懶,而且x64部分刪除空格后过椎,它的第一個片段也是5行室梅,加上x64 Windows10這一行,還是構(gòu)成一個完整的6行,并不會導(dǎo)致數(shù)據(jù)發(fā)生紊亂亡鼠,所以懶得對它進(jìn)行單獨(dú)處理了赏殃。】

回到Table.Pivot()函數(shù)上來间涵。

第三個和第四個參數(shù)其實(shí)就是原始表格中仁热,記錄(Record)的屬性(Attribute)和值(Value)。透視后勾哩,產(chǎn)生的每一條記錄抗蠢,其屬性來源于第三個參數(shù),其值來源于第四個參數(shù)思劳。在這個例子中迅矛,每一條記錄的屬性都在原始表格第二部分?jǐn)?shù)據(jù)的第一列,值都在第二列潜叛。

屬性和值

如果用PowerQuery的角度來理解表格(Table)和記錄(Record)會比較好理解秽褒。可以參見微軟官方網(wǎng)站的Table.Pivot()示例:https://msdn.microsoft.com/zh-cn/library/mt260767

let
源 = 原始,
刪除的頂端行 = Table.Skip(源,List.PositionOf(Table.Column(源,"File name"),"Additional file information")+4),
刪除的列 = Table.RemoveColumns(刪除的頂端行,{"File size", "Date", "Time", "Platform", "SP requirement", "Service branch", ""}),
刪除的頂端行1 = Table.Skip(刪除的列,6),
已刪除的空行 = Table.SelectRows(刪除的頂端行1, each not List.IsEmpty(List.RemoveMatchingItems(Record.FieldValues(_), {"", null}))),
已添加索引 = Table.AddIndexColumn(已刪除的空行, "索引", 1, 1),
ID = Table.AddColumn(已添加索引, "ID", each Number.IntegerDivide([索引]-1,6)),
重命名的列 = Table.RenameColumns(ID,{{"File name", "Column1"}, {"File version", "Column2"}}),
刪除的其他列 = Table.SelectColumns(重命名的列,{"ID", "Column1", "Column2"}),
已透視列 = Table.Pivot(刪除的其他列,List.Distinct(Table.Column(刪除的其他列,"Column1")), "Column1","Column2"),
刪除的列1 = Table.RemoveColumns(已透視列,{"x64 Windows 10", "ID"})
in
刪除的列1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末威兜,一起剝皮案震驚了整個濱河市销斟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牡属,老刑警劉巖票堵,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扼睬,死亡現(xiàn)場離奇詭異逮栅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)窗宇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門措伐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人军俊,你說我怎么就攤上這事侥加。” “怎么了粪躬?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵担败,是天一觀的道長。 經(jīng)常有香客問我镰官,道長提前,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任泳唠,我火速辦了婚禮狈网,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己拓哺,他們只是感情好勇垛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著士鸥,像睡著了一般闲孤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上础淤,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天崭放,我揣著相機(jī)與錄音,去河邊找鬼鸽凶。 笑死币砂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的玻侥。 我是一名探鬼主播决摧,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼凑兰!你這毒婦竟也來了掌桩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤姑食,失蹤者是張志新(化名)和其女友劉穎波岛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體音半,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡则拷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了曹鸠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煌茬。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖彻桃,靈堂內(nèi)的尸體忽然破棺而出坛善,到底是詐尸還是另有隱情,我是刑警寧澤邻眷,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布眠屎,位于F島的核電站,受9級特大地震影響肆饶,放射性物質(zhì)發(fā)生泄漏改衩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一抖拴、第九天 我趴在偏房一處隱蔽的房頂上張望燎字。 院中可真熱鬧腥椒,春花似錦、人聲如沸候衍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛉鹿。三九已至滨砍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妖异,已是汗流浹背惋戏。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留他膳,地道東北人响逢。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像棕孙,于是被迫代替她去往敵國和親舔亭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容