置頂

我的 VPython 教學文件 (HackMD 版本)

VPython 教學文件目錄 安裝及測試 基本語法 等速度直線運動 自由落下 終端速度 水平抛射 使用For迴圈計算水平抛射資料 斜向抛射 圓周運動 簡諧運動 單擺 木塊彈簧系統分離 重力及簡諧 行星運動 相疊木塊 雙重簡諧運動 一維彈性碰撞 ...

熱門文章

2021年7月20日 星期二

使用 Pandas 從網頁讀取衛星軌道資料並儲存成 csv 檔

作者:王一哲
日期:2021/7/20


前言


我以前寫過一篇關於克卜勒第三行星運動定律的文章,為了將各行星的衛星資料整理成可用的 csv 檔,當時是將表格複製到 LibreOffice Calc 裡面再手動整理,但是使用 Pandas 可以更自動化,以下是我試出來的方法。


程式碼


為了避免找不到儲存後的 csv 檔案,建議於 Python Shell 中用以下指令查詢目前所在的路徑。
import os
os.getcwd()
如果使用 Python 預設的 IDLE,在 Windows 10 預設的路徑是
C:\Users\[UserName]\AppData\Local\Programs\Python\[PythonXX]
在 Ubuntu 預設的路徑是
/home/[UserName]
如果在 Windows 10 可以用以下指令將路徑切換到桌面
os.chdir("C:\\Users\\[UserName]\\Desktop")

接下來從 Jovian Satellite Fact Sheet 網頁的表格中取得木星衛星軌道資料,在網頁上點擊滑鼠右鍵檢視網頁原始碼,於原始碼中搜尋table class會看到有兩個表格,名稱分別為 bulkorbital,我們需要的是衛星軌道資料,因此讀取時需要加上 attrs={'class':'orbital'};為了將最上面的列當作欄位標題,需要加上 header=0
import pandas as pd
table = pd.read_html('https://nssdc.gsfc.nasa.gov/planetary/factsheet/joviansatfact.html', attrs={'class':'orbital'}, header=0)

將資料複製到 data 中,格式為 pandas.DataFrame,再將 data 儲存成 data.csv。
data = table[0].copy()
data.to_csv('data.csv')

如果用文字編輯器打開 data.csv 會看到以下的檔案。
,Unnamed: 0,Semi-major axis(103km),Semi-major axis(Jovian Radii),OrbitalPeriod*(days),RotationPeriod(days),Inclination(degrees),Eccentricity
0,,,,,,,
1,Galilean Satellites,,,,,,
2,Io (JI),421.8,5.91,1.769138,S,0.04,0.004
3,Europa (JII),671.1,9.4,3.551181,S,0.47,0.009000000000000001
4,Ganymede (JIII),1070.4,14.97,7.154553,S,0.18,0.001
5,Callisto (JIV),1882.7,26.33,16.689017,S,0.19,0.006999999999999999
...
92,S/2018 J1,11483,160.6,252.0,,30.61,0.094

從 csv 檔中可以看到每個欄位的名稱,如果想要直接在 Python Shell 裡印出欄位名稱可以使用以下的指令。
for col in data.columns: print(col)

目前的欄位名稱為
Unnamed: 0
Semi-major axis(103km)
Semi-major axis(Jovian Radii)
OrbitalPeriod*(days)
RotationPeriod(days)
Inclination(degrees)
Eccentricity
由於網頁的表格中,最左側的欄位沒有標題,pandas 會將這欄的名稱設定為 Unnamed: 0,使用以下指令將此欄的名稱改為 Name,指令最後的 inplace=True 是為了將修改後的資料儲存到 data 當中。
data.rename(columns={'Unnamed: 0': 'Name'}, inplace=True)

由於表格中有一些空白列,還有4列只有衛星分類的名稱 Galilean Satellites、Lesser Satellites、Unnamed Satellites、Recently Discovered Satellites,可以用以下的指令刪除這些資料。
data.dropna(subset=['Semi-major axis(103km)'], inplace=True)
如果只要刪除整列都是空白的資料,語法為
data.dropna(how='all', inplace=True)

由於 Name 欄位的某些資料有逗號,雖然可以儲存成 csv 檔,但是要再從 csv 檔讀取資料時會遇到麻煩,需要先刪除這些逗號。
data['Name'] = data['Name'].replace(',', '', regex=True)

在欄位 OrbitalPeriod*(days) 當中有許多的資料數字後面多了一個字母 R,這是網頁上用來標示該衛星為公轉方向為逆行 (retrograde),但是我只想留下數字,並在 data 最後面新增欄位記錄這個衛星是否為逆行。使用以下指令新增欄位 Retrograde,並將值設定為 False
data['Retrograde'] = False
找出 OrbitalPeriod*(days) 欄位資料以 R 結尾的列,並將 Retrograde 欄位資料改為 True,如果沒有先新增欄位並設定預設值而是直接使用以下指令,則其它列的 Retrograde 欄位值為 NaN
data.loc[data['OrbitalPeriod*(days)'].str.endswith('R'), 'Retrograde'] = True
刪除 OrbitalPeriod*(days) 欄位資料結尾的R
data['OrbitalPeriod*(days)'] = data['OrbitalPeriod*(days)'].map(lambda x: x.rstrip('R'))

以下是完整的程式碼。
import pandas as pd

planet = 'Juipter'
table = pd.read_html('https://nssdc.gsfc.nasa.gov/planetary/factsheet/joviansatfact.html', attrs={'class':'orbital'}, header=0)
data = table[0].copy()
data.rename(columns={'Unnamed: 0': 'Name'}, inplace=True)
data.dropna(subset=['Semi-major axis(103km)'], inplace=True)
data['Name'] = data['Name'].replace(',', '', regex=True)
data['Retrograde'] = False
data.loc[data['OrbitalPeriod*(days)'].str.endswith('R'), 'Retrograde'] = True
data['OrbitalPeriod*(days)'] = data['OrbitalPeriod*(days)'].map(lambda x: x.rstrip('R'))
data.to_csv(planet+'SatelliteData.csv')

結語


因為木星、土星、天王星的衛星資料網頁格式幾乎一樣,只要將行星的名稱及讀取資料的網址改掉,就可以將土星、天王星的衛星資料也儲存成 csv 檔。以下是木星、土星、天王星的衛星 log a - log T 關係圖,斜率都很接近 2/3。
木星衛星 log a - log T 關係圖

土星衛星 log a - log T 關係圖

天王星衛星 log a - log T 關係圖



行星衛星資料


  1. Jovian Satellite Fact Sheet
  2. Saturnian Satellite Fact Sheet
  3. Uranian Satellite Fact Sheet



參考資料


  1. https://stackoverflow.com/questions/8248397/how-to-know-change-current-directory-in-python-shell
  2. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_html.html
  3. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rename.html
  4. https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html
  5. https://stackoverflow.com/questions/56947333/how-to-remove-commas-from-all-the-column-in-pandas-at-once
  6. https://stackoverflow.com/questions/50372272/how-to-add-columns-to-an-empty-pandas-dataframe
  7. https://pandas.pydata.org/docs/reference/api/pandas.Series.str.endswith.html
  8. https://stackoverflow.com/questions/13682044/remove-unwanted-parts-from-strings-in-a-column





2021/7/21 補充

由於海王星衛星資料的表格比較不一樣,需要稍微修改一下程式碼,忽略表格最上面的列,再手動為每個欄命名,主要修改的部分是第4、5、7、8行程式碼。
import pandas as pd

planet = 'Neptune'
table = pd.read_html('https://nssdc.gsfc.nasa.gov/planetary/factsheet/neptuniansatfact.html', 
                     attrs={'class':'orbital'}, skiprows=1)
data = table[0].copy()
data.columns = ['Name', 'Semi-major axis(103km)', 'Semi-major axis(Jovian Radii)', 'OrbitalPeriod*(days)', 
                'RotationPeriod(days)', 'Inclination(degrees)', 'Eccentricity']
data.dropna(subset=['Name'], inplace=True)
data['Name'] = data['Name'].replace(',', '', regex=True)
data['Retrograde'] = False
data.loc[data['OrbitalPeriod*(days)'].str.endswith('R'), 'Retrograde'] = True
data['OrbitalPeriod*(days)'] = data['OrbitalPeriod*(days)'].map(lambda x: x.rstrip('R'))
data.to_csv(planet+'SatelliteData.csv')

海王星衛星 log a - log T 關係圖


HackMD 版本連結:https://hackmd.io/@yizhewang/r1gqDaQR_

沒有留言:

張貼留言