In [1]:
from graphviz import Digraph
from math import isnan

In [2]:
import pandas

In [3]:
def get_info(url):
    原始資料 = pandas.read_html(url, encoding="utf8")[1]
    學程 = 原始資料[原始資料[2].notnull()]
    學程=學程.set_index(學程[0]).drop(0, 1)
    學程.columns = 學程.iloc[0]
    學程 = 學程[1:]
    學程['學分']=學程['學分'].astype(float)
    return 學程

In [4]:
# 104 學年度應數基礎學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000003,B,AM___1042971'
基礎 = get_info(url)
# 印出結果,其他學程省略
基礎


Out[4]:
科目名稱 英文科目名稱 科目代碼 學分 年級 學期 修別 *先修科目或#背景科目 備註
0
微積分(一) Calculus (I) AM__10000 4 NaN NaN
微積分(二) Calculus(II) AM__10200 4 #微積分(一) NaN
線性代數(一) Linear Algebra (I) AM__10600 3 NaN NaN
線性代數(二) Linear Algebra (II) AM__10900 3 #線性代數(一) NaN
數學導論 Introduction to mathematics AM__11000 3 NaN NaN
程式設計(一) Introduction to Computer Programming (I) CSIE10400 3 NaN NaN
軟體實作與計算實驗 Software Implementations and Computation Exper... AM__11300 3 NaN NaN
計算機概論 Introduction to Computer Science CSIE10200 3 NaN NaN

In [5]:
# 104 學年度數學科學組核心(一)學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000007,B,AM___1042971'
數科組核心一  = get_info(url)

In [6]:
# 104 學年度數學科學組核心(二)學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000008,B,AM___1042971'
數科組核心二  = get_info(url)

In [7]:
# 104 學年度統計資料分析學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000010,B,AM___1042971'
統計資料分析  = get_info(url)

In [8]:
# 104 學年度數學學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000014,B,AM___1042971'
數學  = get_info(url)

In [9]:
# 104 學年度資訊計算學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000013,B,AM___1042971'
資訊計算  = get_info(url)

In [10]:
# 104 學年度數學科學學程
url = 'http://sys.ndhu.edu.tw/aa/class/RuleSearch/RuleDetail.aspx?id=104,1040000119,B,AM___1042971'
數學科學  = get_info(url)

In [11]:
數科組學程名稱 = ['基礎', '數學', '數學科學', '數科組核心一', '數科組核心二', '統計資料分析', '資訊計算']
數科組學程 ={x:globals()[x] for x in 數科組學程名稱}

In [12]:
for k in 基礎.iloc[1].keys():
    print(k+":", 基礎.iloc[1][k])


英文科目名稱: Calculus(II)
科目代碼: AM__10200
學分: 4.0
年級: 一
學期: 下
修別: 必
*先修科目或#背景科目: #微積分(一)
備註: nan

In [13]:
course_data = {}
for k,x in 數科組學程.items():
    for i in x.index:
        if i not in course_data:
            #print(i, x.loc[i])
            course_data[i]=dict(x.loc[i])
            course_data[i]['學程'] = [k]
            if not isinstance(course_data[i]['*先修科目或#背景科目'], str):
                course_data[i]['*先修科目或#背景科目'] = ([], [], [], set())
            else:
                s = course_data[i]['*先修科目或#背景科目'].split('/')
                先修 = [y[1:] for y in s if y[0]=='*']
                背景 = [y[1:] for y in s if y[0]=='#'] 
                其它 = []
                多餘 = set()
                course_data[i]['*先修科目或#背景科目'] =(先修, 背景, 其它, 多餘)
        else:
            course_data[i]['學程'].append(k)
            if x.loc[i]['修別'] == '必':
                course_data[i]['修別'] = '必'

In [14]:
course_data['數值方法']['學程'].append('資訊計算') # 吳建銘老師建議

趙老師建議

高等線性代數 --> 迴歸分析 <--> 實驗設計 (三下,非三上) 高等線性代數 --> 隨機模型

機率論 -> 迴歸分析
數理統計(一) -> 迴歸分析

高等線性代數 --> 隨機模型

微積分、高等線性代數、微分方程 --> 數值方法AB (三上)


In [22]:
新增列表 = {'代數(一)': ['線性代數(二)'],
 '作業系統': ['程式設計(二)'],
 '偏微分方程': ['高等線性代數', '高等微積分(二)', '微分方程'],
 '傅氏分析': ['高等微積分(二)'],
 '動態系統': ['高等線性代數', '高等微積分(二)'],
 '向量分析': ['微積分(二)'],
 '對局論': ['高等微積分(二)'],
 '微分方程': ['線性代數(二)'],
 '微陣列基因資料分析': ['數值方法'],
 '拓樸學': ['高等微積分(一)'],
 '數值方法': ['微積分(二)', '高等線性代數', '微分方程'],
 '數論': ['數學導論'],
 '演算法': ['程式設計(二)'],
 '程式設計(一)': ['計算機概論'],
 '程式設計(二)': ['程式設計(一)'],
 '統計軟體與實務應用': ['軟體實作與計算實驗', '程式設計(二)'],
 '資料結構': ['程式設計(一)'],
 '軟體實作與計算實驗': ['數學導論'],
 '集合論': ['分析導論'],
 '離散數學': ['數學導論'],
  '迴歸分析': ['高等線性代數', '機率論', '數理統計學(一)','實驗設計'],
   '隨機模型': ['高等線性代數'],
     '高等微積分(一)':   ['分析導論']
       }
for y in 新增列表.keys():
    for x in 新增列表[y]:
        l = course_data[y]['*先修科目或#背景科目'][2]
        s = set(l)
        s.add(x)
        l[:] = list(s)

In [23]:
course_data.keys()


Out[23]:
dict_keys(['高等微積分(二)', '基礎機率', '統計軟體與實務應用', '傅氏分析', '精算學', '高等微積分(一)', '軟體實作與計算實驗', '數值方法', '實變函數論', '數理統計學(一)', '應數專論', '複變函數論', '偏微分方程', '數論', '微分方程', '代數(二)', '微積分(一)', '分析導論', '高等線性代數', '統計學', '對局論', '數學導論', '資料結構', '實驗設計', '隨機模型', '向量分析', '迴歸分析', '代數(一)', '集合論', '線性代數(一)', '演算法', '其它選修(二)', '其它選修(一)', '線性代數(二)', '機率論', '程式設計(二)', '幾何', '作業系統', '程式設計(一)', '計算機概論', '動態系統', '數理統計學(二)', '微積分(二)', '離散數學', '時間序列', '其它選修(三)', '數學規劃', '微陣列基因資料分析', '拓樸學'])

In [24]:
course_data['數值方法']


Out[24]:
{'*先修科目或#背景科目': ([],
  ['線性代數(一)', '計算機概論'],
  ['微積分(二)', '微分方程', '高等線性代數'],
  {'微積分(一)', '微積分(二)', '線性代數(一)', '線性代數(二)'}),
 '修別': '必',
 '備註': nan,
 '學分': 3.0,
 '學期': '下',
 '學程': ['統計資料分析', '數學科學', '數科組核心一', '資訊計算'],
 '年級': '三',
 '科目代碼': 'AM__30000',
 '英文科目名稱': 'Numerical Methods'}

In [25]:
# 去除多餘
for i in range(10):
    for k,v in course_data.items():
        for g0 in v['*先修科目或#背景科目'][0]+v['*先修科目或#背景科目'][1]+v['*先修科目或#背景科目'][2]:
                for g2 in course_data[g0]['*先修科目或#背景科目']:
                    for g3 in g2:
                        v['*先修科目或#背景科目'][-1].add(g3)

In [26]:
新增列表2 = {'實驗設計': ['迴歸分析'],
       }
for y in 新增列表2.keys():
    for x in 新增列表2[y]:
        l = course_data[y]['*先修科目或#背景科目'][2]
        s = set(l)
        s.add(x)
        l[:] = list(s)

In [27]:
dot = Digraph(comment="課程地圖",  graph_attr={"rankdir":"LR",  "ratio":"compress", "resolution":"50", "bb":"0,0,187,207"},
              node_attr={"fontname":"Noto Sans CJK TC", "rank":"source", "pin":"true"})

數科組學程顏色 = {'基礎':"#aaaaaa", 
                 '數學': "red", 
                 '數學科學': "#7f5500", 
                 '數科組核心一': "grey", 
                 '數科組核心二': "black", 
                  '統計資料分析': "blue", 
                  '資訊計算': "green"}
數科組學程符號 = {'基礎':"🏗", 
                 '數學': "π", 
                 '數學科學': "🔬", 
                 '數科組核心一': "💙", 
                 '數科組核心二': "💕", 
                  '統計資料分析': "📊", 
                  '資訊計算': "💻"}

年級 = "一二三四"
level = [i+t for i in 年級 for t in "上下"]
for k,v in course_data.items():
    if k.startswith("其它選修") or k=="應數專論":
        continue
    shape = "box"
    style = "filled"
    if v['修別'] == '選':
        style += ", rounded"
    color = {'一': "#ffeeee", "二": "#ffeeff", "三": "#ddddee", "四": "#ccddff"}[v['年級']]
    l = '''<
    %s <BR /> %s>'''%(k, 
            " ".join("<FONT POINT-SIZE='12' color='%s'>%s</FONT>"%(數科組學程顏色[z],數科組學程符號[z]) for z in sorted(v['學程'])))
    dot.node(k,  l, fillcolor = color, style=style, shape=shape)


dot.node('圖例', """< <TABLE>
    %s </TABLE>>"""%("".join("<TR><TD><FONT POINT-SIZE='12' color='%s'>%s</FONT></TD><TD>%s</TD></TR>"%(數科組學程顏色[k], v, k) for k,v in 數科組學程符號.items())), 
         shape="plaintext", margin='none', pos="100,100!", pin="true")
dot.edge('代數(二)', '圖例', style='invis')
for n in level:
    dot.node(n,n, style="invis")
dot.edge("一上", "數學導論", style="invis")
dot.edge("一上", "微積分(一)", style="invis")
dot.edge("一上", "計算機概論", style="invis")

#for i in range(len(level)-1):
#    dot.edge(level[i], level[i+1], style="invis")
for k,v in course_data.items():
    #if not v['*先修科目或#背景科目'][0]+v['*先修科目或#背景科目'][1]+v['*先修科目或#背景科目'][2]:
    #    dot.edge(v['年級']+v['學期'], k , style="invis")
    for g in v['*先修科目或#背景科目'][0]:
        if g not in v['*先修科目或#背景科目'][-1]:
            dot.edge(g, k, color="blue")
    for g in v['*先修科目或#背景科目'][1]:
        if g not in v['*先修科目或#背景科目'][-1]:
            dot.edge(g, k, color="#007f00")
    for g in v['*先修科目或#背景科目'][2]:
        if g not in v['*先修科目或#背景科目'][-1]:
            dot.edge(g, k, color="red")
dot


Out[27]:
%3 高等微積分(二)    高等微積分(二) 💙 傅氏分析    傅氏分析 π 🔬 💕 高等微積分(二)->傅氏分析 實變函數論    實變函數論 π 🔬 💕 高等微積分(二)->實變函數論 複變函數論    複變函數論 π 🔬 💕 高等微積分(二)->複變函數論 偏微分方程    偏微分方程 π 🔬 💕 高等微積分(二)->偏微分方程 對局論    對局論 π 🔬 💕 高等微積分(二)->對局論 動態系統    動態系統 π 🔬 💕 高等微積分(二)->動態系統 基礎機率    基礎機率 🔬 💕 💻 精算學    精算學 🔬 📊 基礎機率->精算學 統計學    統計學 🔬 💕 💻 基礎機率->統計學 隨機模型    隨機模型 🔬 💕 📊 基礎機率->隨機模型 機率論    機率論 π 🔬 💕 📊 基礎機率->機率論 統計軟體與實務應用    統計軟體與實務應用 🔬 📊 高等微積分(一)    高等微積分(一) 💙 高等微積分(一)->高等微積分(二) 拓樸學    拓樸學 π 🔬 💕 高等微積分(一)->拓樸學 軟體實作與計算實驗    軟體實作與計算實驗 🏗 軟體實作與計算實驗->統計軟體與實務應用 數值方法    數值方法 🔬 💙 📊 💻 微陣列基因資料分析    微陣列基因資料分析 🔬 💻 數值方法->微陣列基因資料分析 數理統計學(一)    數理統計學(一) 🔬 💕 📊 數理統計學(二)    數理統計學(二) 🔬 📊 數理統計學(一)->數理統計學(二) 數論    數論 π 🔬 💙 微分方程    微分方程 π 🔬 💙 💻 微分方程->數值方法 微分方程->偏微分方程 代數(二)    代數(二) π 🔬 💕 圖例 📊 統計資料分析 π 數學 💕 數科組核心二 🔬 數學科學 💻 資訊計算 🏗 基礎 💙 數科組核心一 微積分(一)    微積分(一) 🏗 微積分(二)    微積分(二) 🏗 微積分(一)->微積分(二) 分析導論    分析導論 🔬 💙 代數(一)    代數(一) 💙 分析導論->代數(一) 集合論    集合論 π 🔬 💕 分析導論->集合論 高等線性代數    高等線性代數 π 🔬 💙 📊 高等線性代數->數值方法 高等線性代數->偏微分方程 高等線性代數->隨機模型 高等線性代數->動態系統 統計學->數理統計學(一) 時間序列    時間序列 🔬 📊 💻 統計學->時間序列 數學導論    數學導論 🏗 數學導論->軟體實作與計算實驗 數學導論->數論 數學導論->分析導論 離散數學    離散數學 π 🔬 💕 💻 數學導論->離散數學 資料結構    資料結構 🔬 💻 實驗設計    實驗設計 🔬 📊 向量分析    向量分析 π 🔬 💙 迴歸分析    迴歸分析 🔬 📊 代數(一)->代數(二) 線性代數(一)    線性代數(一) 🏗 線性代數(二)    線性代數(二) 🏗 線性代數(一)->線性代數(二) 演算法    演算法 🔬 💻 線性代數(二)->微分方程 線性代數(二)->高等線性代數 線性代數(二)->代數(一) 幾何    幾何 π 🔬 💕 線性代數(二)->幾何 數學規劃    數學規劃 🔬 💕 📊 線性代數(二)->數學規劃 程式設計(二)    程式設計(二) 🔬 💻 程式設計(二)->統計軟體與實務應用 程式設計(二)->演算法 作業系統    作業系統 🔬 💻 程式設計(二)->作業系統 程式設計(一)    程式設計(一) 🏗 🔬 📊 程式設計(一)->資料結構 程式設計(一)->程式設計(二) 計算機概論    計算機概論 🏗 計算機概論->數值方法 計算機概論->程式設計(一) 微積分(二)->基礎機率 微積分(二)->高等微積分(一) 微積分(二)->微分方程 微積分(二)->向量分析 微積分(二)->向量分析 微積分(二)->幾何

In [28]:
dot.render('數科組課程地圖', view=True)


Out[28]:
'數科組課程地圖.pdf'

In [ ]: