熱門文章

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_

沒有留言:

張貼留言