# Streamplot 範例
> 作者:王一哲
> 日期:2019/12/2
<br />
## 前言
我為了寫〈[均勻帶電圓環的電場](https://hackmd.io/@yizhewang/H1Ahc9xTS)〉找到了 matplotlib 官網上關於 [streamplot 指令的範例](https://matplotlib.org/1.3.1/examples/images_contours_and_fields/streamplot_demo_features.html),範例中利用 numpy 的指令直接產生繪圖資料,但是我需要從 csv 檔中讀取由 VPython 產生的資料,因此我需要改寫範例的程式碼。
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/OKMsg44.png">
<div style="text-align:center">streamplot 指令範例繪圖成果</div>
<br />
## 範例
```python=
import numpy as np
import matplotlib.pyplot as plt
Y, X = np.mgrid[-3:3:100j, -3:3:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)
plt.streamplot(X, Y, U, V, color=U, linewidth=2, cmap=plt.cm.autumn)
plt.colorbar()
plt.savefig('streamplot.png')
plt.show()
```
<br />
原來的範例中底下還有另外兩張圖,但我只是要測試指令及存取資料的方法,因此只留下一張圖。範例程式的第4行使用了 numpy.mgrid 產生 X、Y 資料,其語法為
```python
Y, X = np.mgrid[y軸最小值:y軸最大值:y軸點數+j, x軸最小值:x軸最大值:x軸點數+j]
Y, X = np.mgrid[y軸最小值:y軸最大值:y軸間隔, x軸最小值:x軸最大值:x軸間隔]
```
<br />
因此第4行的功能是將 $-3 \leq x \leq 3$ 及 $-3 \leq y \leq 3$ 各分為100個點,再將這些數值交錯產生兩個2維陣列,其中X為
```python
[[-3. -2.93939394 -2.87878788 ... 2.87878788 2.93939394
3. ]
[-3. -2.93939394 -2.87878788 ... 2.87878788 2.93939394
3. ]
[-3. -2.93939394 -2.87878788 ... 2.87878788 2.93939394
3. ]
...
[-3. -2.93939394 -2.87878788 ... 2.87878788 2.93939394
3. ]
[-3. -2.93939394 -2.87878788 ... 2.87878788 2.93939394
3. ]
[-3. -2.93939394 -2.87878788 ... 2.87878788 2.93939394
3. ]]
```
<br />
Y為
```python
[[-3. -3. -3. ... -3. -3.
-3. ]
[-2.93939394 -2.93939394 -2.93939394 ... -2.93939394 -2.93939394
-2.93939394]
[-2.87878788 -2.87878788 -2.87878788 ... -2.87878788 -2.87878788
-2.87878788]
...
[ 2.87878788 2.87878788 2.87878788 ... 2.87878788 2.87878788
2.87878788]
[ 2.93939394 2.93939394 2.93939394 ... 2.93939394 2.93939394
2.93939394]
[ 3. 3. 3. ... 3. 3.
3. ]]
```
<br />
也可以改用 numpy.meshgrid 達成同樣的效果,將第4行改為以下的程式碼效果相同。
```python
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
```
<br />
## 產生資料並存成 csv 檔
```python=
import numpy as np
Y, X = np.mgrid[-3:3:100j, -3:3:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)
rows = len(X)
cols = len(X[0])
X = X.reshape(1, rows*cols)
Y = X.reshape(1, rows*cols)
U = U.reshape(1, rows*cols)
V = V.reshape(1, rows*cols)
speed = speed.reshape(1, rows*cols)
data = np.concatenate((X, Y, U, V, speed), axis=0).transpose()
np.savetxt('data.csv', data, delimiter=',')
```
<br />
1. 第8、9行:從陣列 X 取得列數 rows 及欄數 cols。
2. 第11 ~ 15行:用 numpy.reshape 將陣列 X、Y、U、V、speed 轉成1列、$rows \times cols$ 欄的2維陣列。
3. 第16行:用 numpy.concatenate 將5個陣列接起來再轉置,將資料存在2維陣例 data 當中,這樣各欄的資料依序是 X、Y、U、V、speed。其中選項 axis=0 是預設值,理論上可以省略。
4. 第17行:用 numpy.savetxt 將陣列 data 的資料用逗號分隔儲存到文字檔 data.csv。
5. 以下是陣列 data 的前10列資料。
```python
[[ -3. -3. -13. -11. 17.02938637]
[ -2.93939394 -2.93939394 -12.64003673 -10.93939394 16.71648493]
[ -2.87878788 -2.87878788 -12.28741965 -10.87878788 16.41123723]
[ -2.81818182 -2.81818182 -11.94214876 -10.81818182 16.11359596]
[ -2.75757576 -2.75757576 -11.60422406 -10.75757576 15.82350948]
[ -2.6969697 -2.6969697 -11.27364555 -10.6969697 15.54092161]
[ -2.63636364 -2.63636364 -10.95041322 -10.63636364 15.26577155]
[ -2.57575758 -2.57575758 -10.63452709 -10.57575758 14.99799369]
[ -2.51515152 -2.51515152 -10.32598714 -10.51515152 14.73751749]
[ -2.45454545 -2.45454545 -10.02479339 -10.45454545 14.48426744]]
```
<br />
## 從 csv 檔讀取資料並繪圖
```python=
import numpy as np
import matplotlib.pyplot as plt
X, Y, U, V, speed = np.loadtxt('data.csv', delimiter=',', unpack=True)
num = int(np.sqrt(len(X)))
X = X.reshape(num, num)[0, :]
Y = Y.reshape(num, num)[0, :]
U = U.reshape(num, num)
V = V.reshape(num, num)
speed = speed.reshape(num, num)
plt.streamplot(X, Y, U, V, color=U, linewidth=2, cmap=plt.cm.autumn)
plt.colorbar()
plt.savefig('streamplotTest.png')
plt.show()
```
1. 第6行:從陣列 X 的長度推算原來的列數及欄數。
2. 第7、8行:用 numpy.reshape 將陣列 X、Y 轉成2維陣列,再取出其中第0列的資料存回 X、Y 當中轉成1維陣列,這是為了符合 streamplot 輸入資料的格式。
3. 第9 ~ 11行:用 numpy.reshape 將陣列 U、V、speed 轉成2維陣列。
4. 第13行:用 streamplot 指令繪圖,繪讀成果與範例一模一樣。
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/OKMsg44.png">
<div style="text-align:center">從 csv 檔讀取資料繪圖成果</div>
<br />
## 結語
1. 用 numpy.mgrid 或 numpy.meshgrid 產生資料很方便,但這兩種的差異實在很微妙,產生的資料排序方式也很難想像。
2. 為了將資料依照欄位存進 csv 檔,還要先改變陣列的格式、連接再轉置,這個方法看起來很怪,但是它至少會成功,我目前還想不更好的方法。
3. 最重要的一點,我找到從 csv 檔讀取資料並繪圖的方法,以後就可以用別的工具產生資料並儲存成 csv 檔,甚至可以處理真實的實驗數據。
<br />
## 參考資料
1. **streamplot_demo_features.py**:https://matplotlib.org/1.3.1/examples/images_contours_and_fields/streamplot_demo_features.html
2. **numpy.mgrid**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.mgrid.html
3. **numpy.meshgrid**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.meshgrid.html
4. CSDN **numpy中mgrid与meshgrid的区别**:https://blog.csdn.net/tymatlab/article/details/79027162
5. **matplotlib.streamplot**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html
6. **numpy.concatenate**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.concatenate.html
7. **numpy.savetxt**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.savetxt.html
8. StackOverflow **plot streamlines with matplotlib from file**:https://stackoverflow.com/questions/55207810/plot-streamlines-with-matplotlib-from-file
---
###### tags:`Python`