動機
我們花費大量的時間將資料從普通的交換格式(比如CSV),遷移到像數組、資料庫或者二進位儲存等高效的計算格式。更糟糕的是,許多人沒有將資料移轉到高效的格式,因為他們不知道怎麼(或者不能)為他們的工具管理特定的遷移方法。
你所選擇的資料格式很重要,它會強烈地影響程式效能(經驗規律表明會有10倍的差距),以及那些輕易使用和理解你資料的人。
當提倡Blaze項目時,我經常說:“Blaze能協助你查詢各種格式的資料。”這實際上是假設你能夠將資料轉換成指定的格式。
進入into項目
into函數能在各種資料格式之間高效的遷移資料。這裡的資料格式既包括記憶體中的資料結構,比如:
列表、集合、元組、迭代器、numpy中的ndarray、pandas中的DataFrame、dynd中的array,以及上述各類的流式序列。
也包括存在於Python程式之外的持久化資料,比如:
CSV、JSON、行定界的JSON,以及以上各類的遠程版本
HDF5 (標準格式與Pandas格式皆可)、 BColz、 SAS、 SQL 資料庫 ( SQLAlchemy支援的皆可)、 Mongo
into項目能在上述資料格式的任意兩個格式之間高效的遷移資料,其原理是利用一個成對轉換的網路(該文章底部有直觀的解釋)。
如何使用它
into函數有兩個參數:source和target。它將資料從source轉換成target。source和target能夠使用如下的格式:
Target Source Example
Object Object A particular DataFrame or list
String String ‘file.csv', ‘postgresql://hostname::tablename'
Type Like list or pd.DataFrame
所以,下邊是對into函數的合法調用:
>>> into(list, df) # create new list from Pandas DataFrame >>> into([], df) # append onto existing list >>> into('myfile.json', df) # Dump dataframe to line-delimited JSON >>> into(Iterator, 'myfiles.*.csv') # Stream through many CSV files >>> into('postgresql://hostname::tablename', df) # Migrate dataframe to Postgres >>> into('postgresql://hostname::tablename', 'myfile.*.csv') # Load CSVs to Postgres >>> into('myfile.json', 'postgresql://hostname::tablename') # Dump Postgres to JSON >>> into(pd.DataFrame, 'mongodb://hostname/db::collection') # Dump Mongo to DataFrame
Note that into is a single function. We're used to doing this with various to_csv, from_sql methods on various types. The into api is very small; Here is what you need in order to get started:
注意,into函數是一個單一的函數。雖然我們習慣於在各種類型上使用to_csv, from_sql等方法來完成這樣的功能,但介面into非常簡單。開始使用into函數前,你需要:
$ pip install into >>> from into import into
在Github上查看into工程。
執行個體
現在我們展示一些更深層次的相同的執行個體。
將Python中的list類型轉換成numpy中的array類型
>>> import numpy as np >>> into(np.ndarray, [1, 2, 3]) array([1, 2, 3])
載入CSV檔案,並轉換成Python中的list類型
>>> into(list, 'accounts.csv') [(1, 'Alice', 100), (2, 'Bob', 200), (3, 'Charlie', 300), (4, 'Denis', 400), (5, 'Edith', 500)]
將CSV檔案轉換成JSON格式
>>> into('accounts.json', 'accounts.csv') $ head accounts.json {"balance": 100, "id": 1, "name": "Alice"} {"balance": 200, "id": 2, "name": "Bob"} {"balance": 300, "id": 3, "name": "Charlie"} {"balance": 400, "id": 4, "name": "Denis"} {"balance": 500, "id": 5, "name": "Edith"}
將行定界的JSON格式轉換成Pandas中的DataFrame格式
>>> import pandas as pd >>> into(pd.DataFrame, 'accounts.json') balance id name 0 100 1 Alice 1 200 2 Bob 2 300 3 Charlie 3 400 4 Denis 4 500 5 Edith
它是如何工作的?
格式轉換是有挑戰性的。任意兩個資料格式之間的健壯、高效的格式轉換,都充滿了特殊情況和奇怪的庫。常見的解決方案是通過一個通用格式,例如DataFrame或流記憶體列表、字典等,進行格式轉換。(見dat)或者通過序列化格式,例如ProtoBuf或Thrift,進行格式轉換。這些都是很好的選擇,往往也是你想要的。然而有時候這樣的轉換是比較慢的,特別是當你在即時計算系統上轉換,或面對苛刻的儲存解決方案時。
考慮一個例子,在numpy.recarray和pandas.DataFrame之間進行資料移轉。我們可以非常快速地,適當地遷移這些資料。資料的位元組不需要更改,只更改其周圍的中繼資料即可。我們不需要將資料序列化到一個交換格式,或轉換為中間的純Python對象。
考慮從CSV檔案遷移資料到一個PostgreSQL資料庫。通過SQLAlchemy(註:一個Python環境下的資料庫工具箱)使用Python迭代器,我們的遷移速度不太可能超過每秒2000條記錄。然而使用PostgreSQL內建的CSV載入器,我們的遷移速度可以超過每秒50000條記錄。花費一整晚的時間和花費一杯咖啡的時間進行資料移轉,是有很大區別的。然而這需要我們在特殊情況下,能足夠靈活的使用特殊代碼。
專門的兩兩互換工具往往比通用解決方案快一個數量級。
Into項目是那些成對地資料移轉組成的一個網路。我們利用展示這個網路:
每個節點是一種資料格式。每個定向的邊是一個在兩種資料格式之間轉換資料的函數。into函數的一個調用,可能會遍曆多個邊和多個中間格式。例如,當我們將CSV檔案遷移到Mongo資料庫時,我們可以採取以下路徑:
?將CSV檔案載入到DataFrame中(利用pandas.read_csv)
?然後轉換為np.recarray(利用DataFrame.to_records)
?接著轉換為一個Python的迭代器類型(利用np.ndarray.tolist)
?最終轉換成Mongo中的資料(利用pymongo.Collection.insert)
或者我們可以使用MongoDB內建的CSV載入器,編寫一個特殊函數,用一個從CSV到Mongo的定向邊縮短整個處理過程。
為了找到最有效路線,我們利用相對成本(引入權重的ad-hoc)給這個網路的所有邊賦予權重值。然後我們使用networkx找到最短路徑,進而進行資料移轉。如果某個邊由於某種原因失敗了(引發NotImplementedError),我們可以自動重新尋找路徑。這樣我們的遷移方法是既高效又健壯的。
注意,我們給某些節點塗上紅色。這些節點的資料量可以大於記憶體。當我們在兩個紅色節點之間進行資料移轉時(輸入和輸出的資料量都可能大於記憶體),我們限制我們的路徑始終在紅色子圖中,以確保遷移路徑中間的資料不會溢出。需要注意的一種格式是chunks(…),例如chunks(DataFrame)是一個可迭代的,在記憶體中的DataFrames。這個方便的元格式允許我們在大資料上使用緊湊的資料結構,例如numpy的arrays和pandas的DataFrames,同時保持在記憶體中資料的只有幾十MB。
這種網路化的方法允許開發人員對於特殊情況編寫專門的代碼,同時確信這段代碼只在正確的情況下使用。這種方法允許我們利用一個獨立的、可分離的方式處理一個非常複雜的問題。中央調度系統讓我們保持頭腦清醒。
曆史
很久以前,我寫過into連結到Blaze的文章,然後我立即就沉默了。這是因為舊的實現方法(網路方法之前)很難擴充或維護,也沒有準備好進入其黃金期。
我很滿意這個網路。意想不到的應用程式經常能夠正常運行,into工程現在也準備好進入其黃金期了。Into工程可以通過conda和pip得到,而獨立於Blaze。它主要的依賴為NumPy、Pandas和NetworkX,所以對於閱讀我部落格的大部分人來說,它算是相對輕量級的。如果你想利用一些效能更好的格式,例如HDF5,你將同樣需要安裝這些庫(pro-tip,使用conda安裝)。
如何開始使用into函數
你應該下載一個最近版本的into工程。
$ pip install --upgrade git+https://github.com/ContinuumIO/into or $ conda install into --channel blaze
然後你可能想要通過該教程的上半部分,或者閱讀該文檔。
又或者不閱讀任何東西,只是試一試。我的希望是,這個介面很簡單(只有一個函數!),使用者可以自然地使用它。如果你運行中出現了問題,那麼我很願意在blaze-dev@continuum.io中聽到它們。