針對IEnumerable已經有多篇文章,本篇介紹如何使用IEnumerable實作ETL. ETL,是英文 Extract-Transform-Load 的縮寫,用來描述将資料從來源端經過萃取(extract)、轉置(transform)、加載(load)至目的端的過程。通常來說,從原始端采集的資料有很多問題,同時可能業務需求與采集的資料格式不相比對,是以就必須實作ETL過程。
ETL可以了解為一條清洗管線,資料從一端流入,從另一端流出。資料量可能很大,是以管線不大可能也沒有必要加載全部内容。同時,一般情況下,從管線流出來的資料會進入新的資料池,很少直接修改到原表。
從管線的概念可以看出,ETL需要構造可組合的鍊條,首先實作一組元件,然後實作可将這些元件組裝為一條ETL管線的架構。IEnumerable一大堆的LINQ擴充,正好幫我們實作了這一思想。
1. 資料的表達
我們先讨論清楚如何表達資料,因為資料處理涉及到動态增減屬性的問題,是以一般的實體類是做不到的,我們采用字典來實作。為此我包裝了一個實作IDictionary<string, object>的類。叫做FreeDocument。它可以簡單表示如下:
是以資料的處理,本質就是對每一個字典對象中的鍵值對進行增删改查。
2 .基本元件
資料清洗元件的基接口是ICollumProcess. 定義如下:
更清晰的說,其實派生出四部分:
(1) 生成器
生成器即提供/産生資料的元件。這可能包括生成一個從0-1000的數,擷取某個資料表中的資料,或從網頁檢索的結果。它的接口可以表示如下:
最主要的方法是Generate,它能夠枚舉出一組資料出來,同時還有可能(有時做不到)得到能夠生成文檔的總數量。
(2)過濾器
過濾器即能夠分析一個文檔是否滿足條件,不滿足則剔除的元件。接口也很簡單:
(3)排序器
顧名思義,對資料實作排序的接口,定義如下:
排序一般需要升序和降序,但排序最大的問題是破壞了管線的單向流動性和虛拟性。最少LINQ的标準實作上,排序是記憶體排序,是以必須把資料全部加載進來才能排序,這嚴重影響了性能。是以目前的排序最好在小資料的情況下進行。
(4)列轉換器
它最重要的元件。整個ETL過程,實質上就是不同的列進行變換,組成另外一些列的過程(列就是鍵值對)。 定義實作如下:
看着很複雜,但其實就是将文檔中的一些列轉換為另外一些列。比如對一個字元串的列進行正則替換,或轉換其資料類型(如從string變成int)。舉個最簡單的HTML編解碼的例子:
3. ETL管線的設計
相信你已經想到,ETL管線的核心就是動态組裝的LINQ了。
一個最基本的ETL管理類,應當具有以下的屬性:
public ObservableCollection<ICollumProcess> CurrentETLTools { get; set; } //目前已經加載的ETL工具
protected List<Type> AllETLTools { get; set; } //所有能夠使用的ETL工具。當然Type隻是此處為了友善了解而設定的,更合适的應該是記錄了元件中繼資料,名字和介紹的擴充類。
以及一個方法:
public IEnumerable<IFreeDocument> RefreshDatas(IEnumerable<IFreeDocument> docuts) //從原始資料轉換為新的資料
那麼,這個函數的實作可以如下定義:
基本實作思路如上。即通過優先級排序所有加載的ETL元件,并提取一部分樣例資料,為元件進行一次初始化。然後通過組裝不同的轉換器,生成器,排序器和過濾器,最後即可組裝為一個新的ienumable對象。注意整個過程都是延遲計算的,隻有在真正需要ETL結果時才會進行實質性的操作。
4. 優化ETL管線和實作虛拟視圖
以上就是ETL的基本思路。但是僅僅做到這些是很不夠的。以下才是這篇文章的核心。
ETL管線破壞了原有集合的特性,原有集合可能是能夠支援索引查詢甚至能夠執行高性能查找的。但ETL将其退化為僅能夠枚舉。枚舉意味着隻能從頭通路到尾,不能回退和索引。要想使用新集合,就隻能通路其前n個元素,或者全部通路。這顯然對一些操作是很不利的。
先考慮索引器。如果能滿足以下條件:
(1) 管線中不包括排序器和過濾器,因為它們使得得集合産生了亂序。
(2) 原始集合能夠支援索引器
(3) 使用的生成器能夠提供生成的大小,同時生成器也能夠實作索引器
(4) 轉換器應當隻實作1到1轉換,沒有額外的副作用。
那麼原始集合和新集合元素的對應關系是可計算的。此時索引器就能發揮作用。在實際使用中,轉換器是用的最多的。條件不可謂不苛刻。
關于高性能查找,我們先不考慮針對複雜的SQL查詢,先考慮那種最簡單的find(item[key]==value)的查詢。但這個條件更加苛刻:
(1) key在原始集合中必須支援高性能查找
(2) 滿足上述索引器的四個條件
(3) 針對key這一列的操作,轉換器必須是可逆的。而且最好能實作1-1映射。
所謂可逆的意思就是說,轉換器能從A轉換為B,同時也能通過結果B反推出結果A。 但這種條件何其苛刻!a*5=b,這樣的操作是可逆的,然而正則轉換,替換以及絕大多數的運算都是不可逆的。
怎麼辦呢?可能的做法,就是轉換器在轉換過程中,就動态地将key的轉換結果儲存下來。于是,對新集合的查找操作,最後就能一步步回退到原始集合的查找操作。還有更好的辦法麼?
如何讓新集合應對複雜的SQL查詢?首先需要解析SQL, 這可能涉及到大量的數學推導和轉換。以至于在實作當中因為限制太多,基本上不可能實作。以篩選key為一定範圍的資料為例,每次都需要逆向推導,這種推導難度非常大。
5. 智能ETL和使用者體驗優化
整個ETL過程,是人為觀察資料的特性,組合和配置不同的ETL元件,這一過程能夠實作自動化嗎?
人是很智能的,它能夠觀察不同資料的格式和類型,發現其中的特征,比如以下資料:
人通過觀察這麼兩行的資料,就可以大概的判斷出這些資訊分别代表的是什麼意思,以及如何去分割和轉換。可以用正則,提取第一個出現的數字,即樓層,再使用\d{4}提取年份,而用逗号分割,即可得到小區名稱。
但是,這個操作依舊需要最少懂得一定程式基礎的人來參與,如果用機器來做的話,又該如何做呢?自動化步驟可以分為兩個層次:
(1) 自動分割和對齊。
資料尤其是來自web的資料,由于本身是由程式生成的,是以在格式上有高度的統一性,同時分隔符也是類似的,包括逗号,分号,空格,斜杠等。是以,可以統計不同分割符出現的次數,以及對應的位置,通過機率模型,生成最可能的分割方案,使得每一條資料分割出來的長度和子項數量盡可能一緻。
(2) 自動識别内容
自動識别内容可以依賴于規則或者識别器。一種比較可靠的方法是通過基于正則的文本規則,構造一組規則組。通常200x這樣的數值,很容易被了解為年份,而12:32這樣的結構,則很容易被識别為時間。通過基于結構的識别引擎,不僅能夠識别”這是什麼内容“,更能提出其中繼資料,比如日期中的日月年等資訊,為之後的工作做準備。
Web表格最大的好處,在于它的格式一緻性。隻要分析很少的具有代表性的樣例資料,就能夠掌握整個資料集的特征。是以完全可以用比較大的代價獲得一個盡可能高的識别子產品,而在執行過程中盡量提升性能。
作者:熱情的沙漠
出處:http://www.cnblogs.com/buptzym/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。