教你用 Python 在筆記本上分析 100GB 數據

文章整理:加米穀大數據

許多組織都想盡可能多地收集和利用數據,從而改進業務、增加收入和提升影響力。因此,數據科學家們要面對 50GB,甚至 500GB 數據集的場景變得越來越普遍。

目前,這些數據集處理起來有點麻煩。就大小而言,它們可以放進你筆記本電腦的硬盤裡,但卻無法裝入內存。所以,僅僅打開和查看它們就很困難,更何況進一步探索和分析。

處理這樣的數據集時,一般有 3 種策略。

第 1 種是對數據進行子抽樣,但它有一個明顯缺點:可能因忽略部分數據而錯失關鍵信息,甚至誤解數據表達的含義。

第 2 種是使用分佈式計算。雖然在某些情況下這是一種有效的方法,但是管理和維護集群會帶來巨大開銷。想象一下,要為一個剛超出內存大小、大概 30-50GB 的數據集就建立一套集群,對我來說,這似乎有點“用力過猛”。

第 3 種是租用一個內存大小等同於數據集大小的強大雲服務實例,例如,AWS 提供了 TB 級內存的雲服務實例。但這種情況還需要管理雲數據存儲空間,並且在每次實例啟動時都要等待數據從存儲空間傳輸到實例。另外還需要應對數據上雲的合規性問題,以及忍受在遠程機器上工作帶來的不便。更別提成本,雖然開始會比較低,但隨著時間推移會快速上漲。

本文向你展示一種全新方法,它更快、更安全,可以更方便、全面地對幾乎任意大小的數據集進行數據科學研究,只要這個數據集能裝進你的筆記本電腦、臺式機或者服務器的硬盤裡就行。

Vaex

教你用 Python 在筆記本上分析 100GB 數據

Vaex 是一個開源的 DataFrame 庫,對於和你硬盤空間一樣大小的表格數據集,它可以有效進行可視化、探索、分析乃至實踐機器學習。

為實現這些功能,Vaex 採用內存映射、高效的核外算法和延遲計算等概念。所有這些都封裝為類 Pandas 的 API,因此,任何人都能快速上手。

10 億級出租車的數據分析

為闡述這些概念,我們對一個遠超出一般筆記本電腦內存大小的數據集進行簡單地探索分析。

這裡,我們使用 New York City(NYC) Taxi 數據集,它包含了標誌性的黃色出租車 2009 年到 2015 年間超過十億次的出租車行程信息。

數據從網站下載,提供 CSV 格式。完整分析可以單獨查看這個 Jupyter notebook 。

0.052 秒打開 100G 的數據集

第一步是將數據轉換為內存映射文件格式,如 Apache Arrow 、 Apache Parquet 或 HDF5 。關於如何把 CSV 數據轉為 HDF5 的例子請看這裡。一旦數據存為內存映射格式,即便它的磁盤大小超過 100GB,用 Vaex 也可以在瞬間打開它(0.052 秒):

教你用 Python 在筆記本上分析 100GB 數據

Vaex 瞬間打開內存映射文件(0.052 秒)

為什麼這麼快?在用 Vaex 打開內存映射文件時,實際上並沒有讀取數據。Vaex 只讀取了文件元數據,比如數據在磁盤上的位置、數據結構(行數、列數、列名和類型)、文件描述等等。那如果想查看或者操作數據呢?

將數據結果展示在一個標準的 DataFrame 中進行預覽,速度一樣非常快。

教你用 Python 在筆記本上分析 100GB 數據

預覽 New York City Yellow Taxi 數據

代碼單元執行時間還是非常短。這是因為展示 Vaex DataFrame 或者某列,只需要從硬盤中讀取前 5 行和後 5 行數據。這裡引出另一個要點:Vaex 只會在必要的時候遍歷整個數據集,而且它會盡可能遍歷更少的數據。

不管怎樣,我們先從異常值和錯誤的輸入值開始清理這個數據集。一種好的方式是用describe方法獲取數據的高級概覽,它可以展示樣本數量、缺失值的數量和每列的數據類型。

如果某列的數據類型是數值,它還將展示其平均值、標準差、最小值及最大值。而所有的這些數據都是通過一次數據遍歷計算的。

教你用 Python 在筆記本上分析 100GB 數據

使用describe方法獲得 DataFrame 的高級概覽,注意這個 DataFrame 包含 18 列數據,不過截圖只展示了前 7 列。

describe方法很好地體現了 Vaex 的能力和效率:所有這些數據都是在我的 MacBook Pro(15 英寸、2018 款、2.6GHz Intel Core i7 和 32G 內存)上在 3 分鐘內計算出來的。其他庫或方法則需要分佈式計算或超過 100GB 的雲服務實例才能完成相同的計算,而有了 Vaex 你需要的只是數據以及擁有幾 GB 內存的筆記本電腦。

查看describe的輸出很容易發現這個數據包含了一些明顯的異常值。

首先從檢查上車點開始,去除異常值最簡單的方法就是繪製出上車點和下車點位置,並且直觀地確定紐約哪些地區是要分析關注的。由於要處理的數據集如此龐大,直方圖是最有效的可視化方法。用 Vaex 創建和展示直方圖及熱力圖相當快,而且圖表還是可交互的!

複製代碼

<code>df.plot_widget(df.pickup_longitude,                df.pickup_latitude,                shape=512,                limits='minmax',               f='log1p',                colormap='plasma')/<code>

一旦通過交互確定紐約哪些區域是要關注的區域後,我們就可以創建一個篩選後的 DataFrame:

教你用 Python 在筆記本上分析 100GB 數據

上述代碼很酷的一點是,它只需很少內存就可以執行!在篩選 Vaex DataFrame 時並不會複製數據。而是隻創建對原始對象的引用,並在其上應用二進制掩碼。用掩碼來選擇哪些行將被顯示以及將來用於計算。這為我們節省了 100GB 的內存,而像現在許多標準數據科學工具則必須得複製數據才行。

現在,來看下passenger_count這一列。單次出租車行程乘坐人數的最大值是 255,這似乎有點誇張。我們來數數每次行程的乘客人數,這裡用value_counts方法很容易實現:

教你用 Python 在筆記本上分析 100GB 數據

在 10 億行數據上使用 value_counts 方法只需要 20 秒!

從上圖可以看出,乘客超出 6 人可能是少見的異常值或者是錯誤的數據輸入。同時還有大量乘客數為 0 的行程。既然不知道這些行程是否合理,就先把它們過濾掉。

教你用 Python 在筆記本上分析 100GB 數據

再用行程距離做一個類似的練習。由於它是一個連續的變量,我們可以繪製出行程距離的分佈情況。行程距離的最小值是負值,而最大值比火星都遠,所以還是限制在一個合理的區間內繪製直方圖。

教你用 Python 在筆記本上分析 100GB 數據

紐約出租車數據行程距離直方圖

從上圖中可以看到,行程數量隨著距離的增加而減少。在距離大約為 100 英里處,分佈有明顯下降。現在,我們用這個作為分界點來消除行程距離的異常值。

教你用 Python 在筆記本上分析 100GB 數據

在行程距離這一列中存在異常值,也因此有了動機查看行程耗費時間和平均速度。數據集中並沒有提供這兩個特徵數據,但是很容易計算得出:

教你用 Python 在筆記本上分析 100GB 數據

上面的代碼塊不需要內存也不消耗執行時間!是因為代碼只會創建虛擬列(virtual columns),這些虛擬列只包含數學表達式,僅在需要時才進行計算。除此之外,虛擬列和其他常規列是一樣的。注意,其他的標準庫可能需要數十 GB 的內存才能實現相同操作。

好了,我們來繪製行程耗費時間的分佈:

教你用 Python 在筆記本上分析 100GB 數據

紐約超過 10 億次出租車行程耗費時間的直方圖

從上圖可以看到,95% 的出租車行程不到 30 分鐘就可以到達目的地,但有些行程可能花費超過 4-5 個小時。你能想象在紐約市被困在出租車裡 3 個多小時的情景麼?不管怎樣,我們豁達點只考慮少於 3 小時的行程:

教你用 Python 在筆記本上分析 100GB 數據

對於出租車的平均速度,也選擇一個合理的範圍來查看:

教你用 Python 在筆記本上分析 100GB 數據

出租車平均速度分佈

根據分佈趨平的位置,可以推斷出租車合理的平均速度在每小時 1 到 60 英里之間,由此可以更新篩選後的 DataFrame:

教你用 Python 在筆記本上分析 100GB 數據

把關注點切換到出租車行程的費用上,從describe方法的輸出可以看到 fare_amount、total_amount 和 tip_amount 列都有一些誇張的異常值。首先,這幾列都不應該出現負值。

另一方面,有些數字表示一些幸運的司機只開一次出租車就快要成為百萬富翁了。讓我們在合理的範圍內查看這些數量的分佈:

教你用 Python 在筆記本上分析 100GB 數據

紐約超過 10 億次出租車行程的車費、總額和小費的分佈。在筆記本上繪製這些圖表只用了 31 秒!

以上三個分佈都有相當長的尾部。尾部可能有一些正確的值,而其他可能都是錯誤的輸入。不管怎樣,我們保守一點只考慮 fare_amount、total_amount 和 tip_amount 低於 200 美元的行程。另外 fare_amount、total_amount 的值還要大於 0。

教你用 Python 在筆記本上分析 100GB 數據

最終,在初步清理之後,看看還剩下多少出租車行程數據可供分析:

教你用 Python 在筆記本上分析 100GB 數據

還剩下 11 億的行程!這些數據足以讓我們從出租車出行中獲得一些有價值的見解。

坐上駕駛座

假設我們是一名出租車司機或者一家出租車公司的經理,有興趣使用這些數據來了解如何最大化利潤、最小化成本或者僅僅是改善我們的工作生活。

首先,我們找出可以帶來平均最高收益的接客地點。簡單講,只需要繪製出接客地點的熱力圖,並用顏色標記平均價格,然後查看熱點地區。但是由於出租車司機需要自行承擔費用,例如燃料費。

因此,雖然將乘客載到較遠的地方可能帶來更高車費,但同時也意味著更多的燃料消耗和時間損失。

另外,在偏遠地區找一個回市中心某地的乘客可不是那麼容易,而在沒有乘客的情況下回程就會很浪費。一種解決方法是用車費和行程距離之比的平均值對熱力圖進行顏色編碼。我們來嘗試一下這兩種方法:

教你用 Python 在筆記本上分析 100GB 數據

紐約熱力圖,顏色編碼:平均車費(左),車費與行程距離的平均比值

簡單情況下,只關心所提供服務的最高車費時,紐約機場以及像範懷克高速公路(Van Wyck Expressway)和長島高速公路(Long Island Expressway)這樣的主幹道是最佳的載客區。

當考慮行程距離時,會得到一張不同的圖像。範懷克高速公路、長島高速公路以及機場仍然是搭載乘客的好地方,但它們在地圖上的重要性要低得多。而在哈德遜河(Hudson river)的西側出現了一些新的熱點地區,看起來利潤頗豐。

作為出租車司機,實踐中可以做得很靈活。要想更好地利用這種靈活性,除了知道要在哪裡載客之外,瞭解什麼時候出車最賺錢也很有用。為回答這個問題,我們繪製一個圖表來展示每天和每小時的平均車費與行程距離之比。

教你用 Python 在筆記本上分析 100GB 數據

一週中每天以及一天中每小時的車費與行程的平均比值

上面的圖很符合常識:高峰時段最掙錢,特別是工作日的中午。作為出租車司機,收入的一部分要交給出租車公司,所以我們可能會對哪天、哪個時段顧客給的小費最多感興趣。繪製一個類似的圖展示平均小費比例:

教你用 Python 在筆記本上分析 100GB 數據

一週中每天以及一天中每小時的小費比例平均值

上圖很有意思,它告訴我們在一週中前幾天的早上 7-10 點和晚上 7-10 點乘客給司機的小費最多。如果在凌晨 3-4 點接乘客,不要指望會有大額小費。

結合最後兩張圖的經驗,早上 8 點到 10 點是最好的工作時間,司機可以得到較多的車費(每英里)和小費。

發動引擎

在本文的前半部分,我們簡要地關注了trip_distance列,在給它清理異常值時,只保留了低於 100 英里的行程。但這個邊界值仍然很大,尤其是考慮到 Yellow Taxi 公司主要在曼哈頓運營。

trip_distance描述出租車從上車點到下車點的行駛距離,但是在確切的兩個上車點和下車點之間,通常有多條不同距離的路線可以選擇,比如為了避開交通堵塞和道路施工的情況。

所以,相對於trip_distance列,我們計算一項接送位置之間可能的最短距離,命名為arc_distance:

教你用 Python 在筆記本上分析 100GB 數據

對於用 numpy 編寫的複雜表達式,vaex 可以藉助 Numba、Pythran 甚至 CUDA(需要 NVIDIA GPU)通過即時編譯來極大提高運算速度。

arc_distance的計算公式非常複雜,它包含了大量的三角函數和數學運算,在處理大型數據集時計算代價非常高。如果表達式或函數只用到了 Python 運算符和 Numpy 庫的方法,Vaex 會使用計算機的所有核心來並行計算。

除此之外,Vaex 通過 Numba (使用 LLVM)和 Pythran (通過 C++ 加速)支持即時編譯從而提供更好性能。如果你碰巧有 NVIDIA 顯卡,就可以通過jit_cuda方法來運用 CUDA 以獲取更快的性能。

我們來繪製 trip_distance 和 arc_distance 的分佈:

教你用 Python 在筆記本上分析 100GB 數據

左:trip_distance 和 arc_distance 的比較;右:arc_distance<100 米時 trip_distance 的分佈。

有意思的是,arc_distance從來沒有超過 21 英里,但出租車實際行駛距離可能是它的 5 倍。事實上,有數百萬次的行程下客點距離上客點只有 100 米(0.06 英里)!

過去幾年的 Yellow Taxis

我們今天使用數據集時間上跨越了 7 年。隨著時間流逝,人們的興趣如何演變可能是件有趣的事。使用 Vaex 可以進行快速的核外分組和聚合操作。

我們來看看 7 年間車費和行程距離都有什麼變化:

教你用 Python 在筆記本上分析 100GB 數據

在四核處理器的筆記本電腦上,對擁有超過 10 億樣本的 Vaex DataFrame 進行 8 個聚合的分組操作只需不到 2 分鐘。

在上面的代碼塊中,我們執行分組操作,然後執行 8 個聚合,其中有 2 個位於虛擬列上。上面的代碼塊在我的筆記本電腦上執行耗時不到 2 分鐘。鑑於我們使用的數據包含超過 10 億條樣本,這是相當驚人的。

總之,我們來看看結果,以下是多年來乘坐出租車的費用的變化情況:

教你用 Python 在筆記本上分析 100GB 數據

每年的平均車費和總金額以及乘客所付的小費比例

可以看出隨著時間流逝,出租車費和小費都在上漲。再來看看出租車每年的平均trip_disrance和arc_distance:

教你用 Python 在筆記本上分析 100GB 數據

出租車每年的行程和弧距

上圖顯示,trip_distance和arc_distance都有一個小的增長,這意味著,平均而言,人們傾向於每年走得更遠一點。

給錢吧

在旅程結束之前,我們再停一站,調查一下乘客是如何支付乘車費用的。數據集中包含了payment_type,來看看它都包含什麼值:

教你用 Python 在筆記本上分析 100GB 數據

從數據集文檔中,可以看出只有 6 個有效條目:

  • 1 = 信用卡支付
  • 2 = 現金付款
  • 3 = 免費
  • 4 = 爭議
  • 5 = 未知
  • 6 = 無效行程

由此,可以簡單地把payment_type映射到整數:

教你用 Python 在筆記本上分析 100GB 數據

現在可以根據每年的數據進行分組,看看紐約人在支付打車費用方面的習慣是如何變化的:

教你用 Python 在筆記本上分析 100GB 數據

每年的支付方式

可以發現隨著時間的推移,信用卡支付逐漸變得比現金支付更加頻繁。我們果然是生活在數字時代!在上面的代碼塊中,一旦完成數據聚合,小型的 Vaex DataFrame 可以輕易地轉換為 Pandas DataFrame,從而傳遞給 Seaborn 。不用費勁在這重新發明輪子。

最後通過繪製現金支付和信用卡支付之間的比例,來查看付款方法是否取決於當天的時間或者星期幾。為此,先創建一個過濾器,篩出用現金或者信用卡的行程。

下一步是我最喜歡的 Vaex 的特性之一:帶選擇的聚合。其他庫要求對每個支付方法篩選出單獨的 DataFrame 進行聚合,然後再合併為一個。

而使用 Vaex,可以在聚合函數中提供多個選擇從而一步到位。這非常方便,只需進行一次數據傳遞,能提供更好性能。之後就可以用標準方式繪製 DataFrame:

教你用 Python 在筆記本上分析 100GB 數據

在給定的時間和星期中某一天,現金和信用卡支付的比例

從上圖可以發現其模式非常類似於之前的一週中每天以及一天中每小時的小費比例。從這兩個圖推測,信用卡支付的乘客傾向於比現金支付的乘客給更多的小費。

以上就是小編整理的內容,希望能幫助到大家!


分享到:


相關文章: