今天Focus在csv和excel的處理。明天介紹JSON。
Pandas是一種in-memory工具,也就是讀檔需要先把資料存在到記憶體,所以書上建議系統的記憶體是dataframe大小的3-10倍,才有餘裕去處理data的運算。
可以利用開放資料網站做測試
CSV講得比較詳細。主要是因為工作上應用的比較多,加上excel跟它能使用的屬性差不多。最基礎的讀csv檔非常直覺,而且一行解決。
df.read_csv()
但是存取檔案的過程常常不是那麼輕鬆的,遇上的問題包含但不限以下
這些問題,pandas也有因應的作法。
要先知道檔案的編碼才能指定用什麼編碼。可以用notepad++開啟檔案查看。
Pandas提供很多屬性可以靈活讀取。
#指定編碼為big5
df = pd.read_csv('YouBike.csv', encoding='big5')
#指定編碼為utf-8-sig, for具有bom的utf8編碼
df = pd.read_csv('meterials.csv', encoding='utf-8-sig')
#將內容為"NA","--","無"的data都設為缺失值
df = pd.read_csv("YouBike.csv", encoding='big5', na_values=["NA","--","無"])
#將id欄設定為索引
df = pd.read_csv("YouBike.csv", index_col="seq")
# 指定欄位
df = pd.read_csv("YouBike.csv", usecols=["id","name"])
# 只讀前 1000 列
df = pd.read_csv("YouBike.csv", nrows=1000)
就是這麼ez!接下來聊聊關於大檔案。
用這個檔案當範例:https://catalog.data.gov/dataset/electric-vehicle-population-data
首先介紹一個指令: df.info()
它會回傳如下資料
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 261698 entries, 0 to 261697
Data columns (total 17 columns):
Index | Column | Non-Null | Count | Dtype |
---|---|---|---|---|
0 | VIN (1-10) | 261698 | non-null | object |
1 | County | 261688 | non-null | object |
2 | City | 261688 | non-null | object |
dtypes: float64(5), int64(2), object(10)
memory usage: 33.9+ MB
可以很快速的知道是否有缺失值要處理,非數值欄位pandas會將其型別轉為object,數值則是int64,
大量的字串會佔用大量的記憶體空間,可以把它轉成category型態,有用過c#或python的朋友,可以把它想成enum,每個data將會被以數值存檔,只是有個對照檔,這樣能節省大量的記憶體空間,並且在groupby / value_counts / merge的運算會更快。
Category 特別適合重複值多、有限種類的欄位,優點是 省記憶體 + 提升效能 + 支援有序比較。
數值的部分則如果確定是短數值,也可以改成Int8。
df = pd.read_csv('Electric_Vehicle.csv', dtype={"County":"category",
"Postal Code":"Int8",
"Model Year":"Int8",
"Make":"category",
"Model":"category",
"Electric Vehicle Type":"category",
"Electric Range":"Int8",
"Base MSRP":"Int8",
"Electric Utility":"category",
"Legislative District":"Int8"})
df.info()
結果memory usage就立刻從33.9+ MB → 16.8+ MB,是不是很amazing!
另一個狀況是它大到不太方便一次load進來,那可以跟zip一樣分割處理,並指定chunksize(每次讀進來的大小)
for chunk in pd.read_csv("big.csv", chunksize=100000):
process(chunk)
可以用的屬性還有很多,我就簡單列一下我目前了解的了。
df.to_csv()
to_csv就比較簡單了,只要注意兩件事:
編碼
Pandas預設會存utf8,但如果你的csv裡面有中文,且這個data不只是給程式讀,還需要用excel開來看的話,建議要存utf-8-sig。上次我才在編碼的深淵掙扎了一天就為了沒有bom的utf8開起來是亂碼。
index
如果程式裡沒有給dataframe set index,可以很輕鬆愉快的to_csv(),但要是有設定的話記得要把index設定成false,不然會多出不必要的欄位
#編碼設定成utf-8-sig且index為false
df.to_csv("output.csv", index=False, encoding="utf-8-sig")
#只存特定欄位
df.to_csv("out.csv", columns=["id","name"], index=False)
#浮點數格式化
df.to_csv("out.csv", float_format="%.2f")
也可以搭配昨天的資料過濾的結果再to_csv,總之就是一個靈活的熊貓師父。
df.read_excel()
多數的屬性跟csv是一樣的,但畢竟是excel,可以指定sheet_name也是合理的,沒有指定sheet_name會預設返回第一個sheet。
#df_0和df_1是一樣der
df_0 = pd.read_excel('ubike_excel.xlsx')
df_1 = pd.read_excel('ubike_excel.xlsx', sheet_name='ubike')
df_2 = pd.read_excel('ubike_excel.xlsx', index_col=0, sheet_name='ubike_2')
#讀多個 sheet(回傳 dict)
dfs = pd.read_excel("ubike_excel.xlsx", sheet_name=["ubike","ubike_2"])
#讀全部 sheet
dfs = pd.read_excel("ubike_excel.xlsx", sheet_name=None)
#指定欄位與列
df = pd.read_excel("ubike_excel.xlsx", usecols=["月份","發布機關名稱"], nrows=100)
寫excel有兩個方式,如果只是要做一個sheet的保存,可以直接dataframe去to_excel,如果是多個sheet就需要先使用pd.ExcelWrite,
df.to_excel()
pd.ExcelWriter()
> df.to_excel()
要使用ExcelWriter需要pip install xlsxwriter
df_1.to_excel("single.xlsx",sheet_name="Summary", index=False)
df_2[df_2["西元年"]==2024].to_excel("single_2024.xlsx", sheet_name="Year2024", index=False)
with pd.ExcelWriter("multi.xlsx", engine="xlsxwriter") as writer:
df_1.to_excel(writer, sheet_name="Summary", index=False)
df_2[df_2["西元年"]==2024].to_excel(writer, sheet_name="Year2024", index=False)
下集預告:明天來講JSON。