如何在樹狀結構資料夾中合併大量CSV?

在將資料匯入資料庫之前,我們必須先做一些適當的處理
並且做一些適當的前處理,才能順利地將資料整合
可以用 tree 這個指令(需另外安裝, apt / brew 皆可安裝),觀察資料夾的結構長什麼樣子

tree -d .

%e8%9e%a2%e5%b9%95%e5%bf%ab%e7%85%a7-2017-02-28-%e4%b8%8a%e5%8d%887-27-53

仔細觀察後,發現是一個樹狀結構
而在每個末端資料夾上都有滿滿的CSV,具備有以下特性

  • 2000多個,每個2MB左右
  • 檔案命名皆為流水號 ( e.g 0001.csv, 0002.csv, …)
  • 檔案資料欄位格式皆相同

於是我決定先把所有末端資料夾內的CSV進行合併,再以資料夾路徑名稱來命名
新建一個資料夾叫data, 把原本的資料都丟進去, 再把合併完成的csv放到output資料夾內

先來看一下code


import os
import pandas as pd
import time
for root, dirs, files in os.walk("data"):
if not dirs:
df_list = []
start = time.time()
print(root)
for file in files:
path = root+"/"+file
if(os.path.getsize(path)) > 600000:
df = pd.read_csv(path, encoding = "ISO-8859-1", header=3)
df = df.dropna(how='all')
df_list.append(df)
if df_list:
df_concat = pd.concat(df_list, ignore_index=True)
df_concat.columns = [c.replace("\n"," ") for c in list(df_concat.columns)]
filename = "_".join(root.split("/")[1:]) + ".csv"
df_concat.to_csv("output/" + filename, index=False)
print("[+] " + filename)
print(round(time.time() – start, 2))

view raw

convert.py

hosted with ❤ by GitHub

這份22行的code實現了以下幾點功能

  • 歷遍所有資料夾,當前root底下沒有其他資料夾(達到末端),讀取root內所有的檔案
    • 確保每一個檔案都大於600kb
      • 在這批資料中,有許多空白的CSV檔案大小約 5xx kb,沒有必要讀取
    • ISO-8859-1編碼讀取(對方匯出資料時的編碼似乎是採用Latin-1)
    • 指定CSV檔的第四行為資料欄位(並且捨棄前三行我們不需要的header)
    • 將所有資料欄位皆為空的row刪除
    • 把處理好的dataframe存入一個list內,繼續處理下一個檔案
  • 處理完所有檔案後,將list內的dataframe全部拼接起來,並且給予新的index
  • 將資料欄位中的\n給拿掉(沒錯,居然有人 資料欄位使用換行符\n…)
  • 將檔案路徑的"/"以"_"取代之,並且捨棄最前面的 “data"字串,最後再給予.csv副檔名
  • 用pandas輸出CSV,儲存在output資料夾內
    • 在這裡輸出時忽略index,因為他不重要
  • 輸出在處理這份csv上,總共花了多少時間

Python實在是太方便了

如何在MySQL上快速匯入大量的巨大csv?

最近拿到幾組資料,大概將近20張的csv,每個檔案都好幾GB

必須趕快把資料匯入MySQL中,方便未來分析使用&趕上明天早上的約會

跟大家分享我自己比較原始的匯資料方法

1.用GUI輔助建立table

先用GUI把資料匯進去,方便設定table名稱、屬性和欄位,確認encoding和type都沒問題

然後馬上取消傳輸,並且把表格內的資料都清空 (truncate)

在GUI上傳輸的速度實在是太悲劇了

2.將CSV上傳至MySQL所在的機器

把資料集中放在某個資料夾內(e.g. data), 並且用scp上傳到雲端主機的/tmp內

scp -C data/* user@example.com:/tmp

-C指的是在上傳前會經過壓縮處理,  詳細可參考Linux下常用的文件传输方式介绍与比较

3.透過Console將 CSV import 到MySQL內

mysql –local-infile=1 -u root -p

這邊特別注意: csv&table名要用引號括起來,不然有些特定字元會導致SQL Error(例如dash)

LOAD DATA LOCAL INFILE ‘/tmp/data.csv’ INTO TABLE “data"
FIELDS TERMINATED BY ‘,’
ENCLOSED BY ‘"‘
LINES TERMINATED BY ‘\r\n
IGNORE 1 LINES;

這邊就是最雷的地方了,如果你的資料來源是Windows / MSSQL匯出的CSV,這樣是沒問題的

但如果你曾經在把資料丟到MAC上用Pandas做過cleaning / aggregation的話

MySQL 會回傳 Query OK, affect row 0 , 資料怎麼樣都匯不進去

這是因為在Unix/Mac系統的換行字元是\n,  而在Windows上則是\r\n

但如果在Unix/Mac上用Python做過處理後,換行字元自然就會變成\n而非是\r\n結尾

所以必須將上述script的\r\n 修改為 \n,就能順利將資料匯入了

搭配tmux開啟多個MySQL Console,匯入的速度還是挺快的

(好吧其實只是我懶得寫script…)