---
# System prepended metadata

title: 使用 Python 讀取、寫入 ods 的套件 pyexcel-ods3
tags: [LibreOffice, Python]

---

<style>
.markdown-body table{
    display: unset;
}
</style>

# 使用 Python 讀取、寫入 ods 的套件 pyexcel-ods3

> 作者：王一哲
> 日期：2023年8月11日

## 前言

這幾天有一位大學的學長問到如何用 Python 寫 LibreOffice Calc 的巨集，但是目前網路上能找到的資料很少。後來學長想到另一個作法，不要寫巨集，只要用 Pythoon 程式從 ods 檔中讀取資料，在程式中處理完資料，再將資料寫入 ods 檔中，應該也能做到類似的效果。我在網路上找到一些套件，其中一個就是 pyexcel-ods3，經過測試後可以成功地讀取、寫入 ods 檔，以下是簡單的筆記。
<br />

## 安裝套件

如果只要處理 ods 檔，可以在命令列界面中輸入以下指令安裝套件
```shell
pip3 install pyexcel-ods3
```

如果還要處理其它格式的檔案，例如 xlsx，可以在命令列界面中輸入以下指令安裝套件
```shell
pip3 install pyexcel
```

由於處理資料時使用 NumPy ndarray 會比較方便，建議安裝 NumPy。
```shell
pip3 install numpy
```

雖然在以下的程式中會使用到 json 及 collections，但這兩個套件是預設的，不需要另外安裝。
<br />

## 基本語法

以下的程式碼中，假設已經引入函式庫
```python
import pyexcel_ods3 as pe
import json
import numpy as np
from collections import OrderedDict
```
<br />

### 由 ods 檔讀取資料

語法為
```python
輸入資料 = pe.get_data("檔名", start_row=索引值, row_limit=列數)
json.dumps(輸入資料)
```

1. 如果 ods 檔與程式碼放在同一個資料夾中，可以只輸入檔名。
2. start_row：從 0 開始算，如果 start_row＝2 會從 ods 檔第3列開始讀取資料。
3. row_limit：讀取資料列數。
4. get_data 讀到的資料格式是 json，要用 json.dumps 將資料轉成 OrderedDict，key 值為分頁名稱，對應的 value 為二維 list。
5. pyexcel 的概念是將一個分頁當作很大的二維陣列，所以讀取、寫入的分頁資料都會是二維 list，例如
```python
[[1], [2], [3]]  # 只有1欄、內容為1、2、3
[[1, 2, 3]]      # 只有1列、內容為1、2、3
```
<br />

### 更新要寫入 ods 檔的資料

語法為
```python
輸出資料 = OrderedDict()
輸出資料.update({"分頁名稱": 二維 list 資料})
```

資料格式是二維 list，如果使用 NumPy ndarray 會回傳錯誤訊息。將輸出資料寫入 ods 檔時會覆蓋掉原來的檔案內容，無法指定只寫入某個分頁，因此要記得將原來在檔案中的資料也用 update 加到輸出資料中。
<br />

### 將輸出資料寫入 ods 檔

語法為
```python
save_data("檔名", 輸出資料)
```
如果執行程式時要寫入的 ods 檔已開啟，要先關閉再開啟 ods 檔才能看到寫入資料後的內容。
<br />

## 範例程式碼

### 由指定分頁讀取一欄資料，將處理後的資料寫到另一個分頁

假設檔案 test.ods 只有一個名為 Input 的分頁，只有 A 欄中有資料，資料為整數 1 到 10。以下的程式碼會從 Input 分頁讀取 A 欄資料，將資料存入 NumPy ndarray x，再將 x 乘以 2 之後存入 y，最後將 y 寫入 Output 分頁的 A 欄。

```python=
from pyexcel_ods3 import save_data, get_data
import json
import numpy as np
from collections import OrderedDict
# 由 ods 檔讀取並處理資料
dataIn = get_data("test.ods")
print(json.dumps(dataIn))
x = np.asarray([d[0] for d in dataIn["Input"]])
y = 2*x
x = x.reshape(len(x), 1).tolist()
y = y.reshape(len(y), 1).tolist()
# 將資料寫入 ods 檔
dataOut = OrderedDict()
dataOut.update({"Input": x})
dataOut.update({"Output": y})
save_data("test.ods", dataOut)
```
<br />

1. 第8行：由於 dataIn["Input"] 的值為二維 list，先用 for 迴圈取出 d[0] 的資料，再用 np.asarray 轉成 NumPy ndarray 指定給變數 x。
2. 第10、11行：如果 x、y 原來的長度為 N，先用 reshape 將 x、y 轉成 $N \times 1$ 的二維 ndarray，再用 tolist() 轉換成 list，最後分別指定給 x、y。
3. 第16行：用 save_data() 將資料 dataOut 寫入 test.ods。

<br />

### 由指定分頁讀取2欄資料，將處理後的資料寫到另一個分頁

假設檔案 test2.ods 只有一個名為 Input 的分頁，A、B 欄中有皆資料。以下的程式碼會從 Input 分頁讀取 A、B 欄資料，將 A 欄資料存入 x，再將 x 乘以 2 之後存入 z；將 B 欄資料存入 y，再將 y 乘以 3 之後存入 a；最後將 z、a 寫入 Output 分頁的 A、B 欄。

<div style="text-align:center">輸入、輸出資料</div>

<div style="text-align:center">
<table>
    
| <center>Input A 欄</center> | Input B 欄 | Output A 欄 | Output B 欄 |
| --------- | --------- | --------- | --------- |
| 1 | 0.1 |  2 | 0.3 |
| 2 | 0.2 |  4 | 0.6 |
| 3 | 0.3 |  6 | 0.9 |
| 4 | 0.4 |  8 | 1.2 |   
| 5 | 0.5 | 10 | 1.5 |
| 6 | 0.6 | 12 | 1.8 |
| 7 | 0.7 | 14 | 2.1 |
| 8 | 0.8 | 16 | 2.4 |
| 9 | 0.9 | 18 | 2.7 |
|10 | 1.0 | 20 | 3.0 |

</table>
</div>

<br />

```python=
import pyexcel_ods3 as pe
import json
import numpy as np
from collections import OrderedDict
# 由 ods 檔讀取並處理資料
dataIn = pe.get_data("test2.ods")
json.dumps(dataIn)
x = np.asarray([d[0] for d in dataIn["Input"]])
y = np.asarray([d[1] for d in dataIn["Input"]])
z = 2*x
a = 3*y
# 將資料寫入 ods 檔
dataOut = OrderedDict()
dataOut.update({"Input": list(zip(x.tolist(), y.tolist()))})
dataOut.update({"Output": list(zip(z.tolist(), a.tolist()))})
pe.save_data("test2.ods", dataOut)
```
<br />

大部分的程式碼與前一個範例相似，以下只說明第14、15行。由於 x、y、z、a 都是一維 ndarray，需要先轉成二維 list 才能加到 dataOut 當中，可以分為以下3個步驟：

1. 用 tolist() 從 ndarray 轉換成 list。
2. 用 zip() 將轉換後的兩個 list 依序取出元素，組成兩個一組的數組 (tuple)。
3. 用 list() 將一連串的數組轉換成二維 list，例如 x、y 轉換後的資料為
```python
[(1, 0.1), (2, 0.2), (3, 0.3), (4, 0.4), (5, 0.5), (6, 0.6), (7, 0.7), (8, 0.8), (9, 0.9), (10, 1.0)]
```
<br />

## 結語

由於使用 LibreOffice Calc 的人比較少，想要用 Python 讀取、寫入 ods 檔的人更少，網路上相關的文章特別難找，希望這篇筆記能幫助到有需要的人。
<br />

## 參考資料

1. [pyexcel GitHub 頁面](https://github.com/pyexcel/pyexcel)
2. [pyexcel 手冊](https://buildmedia.readthedocs.org/media/pdf/pyexcel/latest/pyexcel.pdf)
3. [pyexcel-ods3 PyPI 頁面](https://pypi.org/project/pyexcel-ods3/)

---

###### tags:`LibreOffice Calc`、`Python`