2021年11月26日 星期五

MongoDB Atlas

IP 地址白名單


為了安全,mongodb atlas 資料庫,除了連線時需要提供使用者名稱與密碼之外,可以設定IP地址(IP Address)白名單。只有列在白名單的電腦可以存取它。

白名單中,可以是單一IP,也可以是CIDR IP 區間(CIDR-notated range of addresses)。

CIDR IP 區間

甚麼是CIDR IP 區間?

舉例來說:
  • 10.0.0.0/24 代表的最小IP是10.0.0.0,最大IP是10.0.0.255
  • 10.0.0.0/16 代表的最小IP是10.0.0.0,最大IP是10.0.255.255
  • 10.0.0.0/8 代表的最小IP是10.0.0.0,最大IP是10.255.255.255
  • 0.0.0.0/0 代表說有的IP
  • 10.0.0.0/32 代表單一IP,即10.0.0.0
摘錄 https://www.ipaddressguide.com/cidr 網站的一段話
CIDR is the short for Classless Inter-Domain Routing, an IP addressing scheme that replaces the older system based on classes A, B, and C. A single IP address can be used to designate many unique IP addresses with CIDR. A CIDR IP address looks like a normal IP address except that it ends with a slash followed by a number, called the IP network prefix. CIDR addresses reduce the size of routing tables and make more IP addresses available within organizations.

該網站也提供CIDR IP 區間對應的最小IP、最大IP與Netmask。

將 Azure Web App 的 IP 地址列入白名單

Azure Web App 要存取 MongoDB Atlas,必須將 App 的 outbound IP 地址列入資料庫的白名單中。

問題來了,App 的 outbound IP 地址不是一個而是一大串(當 App Scale Up/Down 時還會變動)。

我們可以用 Azure Powershell 指令 (Get-AzWebApp -Name gglapp-azure).outboundIpAddresses 來取得 outbound IP 群後,在資料庫端的 Network Access > IP Access IP List > Add Ip Address 裡一筆一筆的的加入白名單內。

請參考 Azure App Service Documentation >  Concepts > Networking > Inbound and outbound IPs 的  https://docs.microsoft.com/en-us/azure/app-service/overview-inbound-outbound-ips 。

2021年10月28日 星期四

了解Azure



Azure 帳號

Build your .NET apps with your Azure free account 建立了Azure 免費帳號。建帳號時,必須提供信用卡資料,先扣款台幣30元確認信用卡的正確性再退款台幣30元。建完免費帳號後,https://portal.azure.com/#home 就是自己在Azure的門戶網站。


Azure for .NET developers

Azure for .NET developers 是學習的起點。


第一個 Web App 發佈成 Azure Application Service

Quickstart: Deploy an ASP.NET web app 示範在Visual Studio 裡建立 Aspnet Web Core App,發布到Azure App Service。
  1. 以 AspNetCore Web App 模板起始沒有認證與Docker功能的Web App。
  2. 發佈到Azure自己的帳號裡。
  3. 用瀏覽器瀏覽到發佈到Azure的Web App。
  4. 更改程式、再次發佈、再次瀏覽。
  5. 到自己的門戶網站管理自己帳戶的資源使用。

第一個 Web App 發佈成 Azure Container Instance 

Quickstart: Docker in Visual Studio 示範將Web App 做成 Docker Container 發佈到 Azure

  1. 用 AspNet Core Web App 模板,選擇 Enable Docker + Docker OS Linux,製成 WebAppDockerAzure 專案。
  2. 若要在 Docker 裡進行偵錯,必須參考 Container Tools launch settings,修改launchSettings.json 裡面關於Docker 的設定。
  3. 專案結點上點擊右鍵,發佈 Docker 映像到 Azure Container Registry
  4. Quickstart: Deploy a container instance in Azure using the Azure portal

第一個由前端與後段組成的 Web App,發佈成 Azure Application Service

Container Tools in Visual Studio 裡面的 Tutorial: Create a multi-container app with Docker Compose 示範如何用 Docker Compose 製作由兩個映像組成的Webp App,講解如何在在地端測試組合效果。

發布映像組的Azure Web App
參考 Create a multi-container (preview) app using a Docker Compose configuration 的 "az webapp create ... --multicontainer-config-type compose ... compose-wordpress.yml" 與
Migrate custom software to Azure App Service using a custom container 的 az role assignment create ..., az resource update ...,製造powershell 指令檔 gglapp-azure-final.ps1。


在下列條件下
  • 已安裝Azure-CLI
  • Powershell 已安裝 Azure Powershell Module(Az)
  • 在地端的Desktop Docker已有 gglcr.azurecr.io/<api>, gglcr.azurecr.io/<front> 兩映像
  • Azure 帳戶內沒有 gglgroup(Azure Resource Group)
  • <compose-azure.ps1> 與 <compose-azure.yml>  在工作目錄中
從 Powershell 內執行 .\<compose-azure.ps1>,可產生 <app-name>(Azure WebApp),之後只要瀏覽到 https://<app-name>.azurewebsites.net 就可使用。

<compose-azure.ps1> 的內容:
# https://docs.microsoft.com/en-us/azure/app-service/tutorial-custom-container?pivots=container-linux#configure-app-service-to-deploy-the-image-from-the-registry
# https://docs.microsoft.com/en-us/azure/app-service/tutorial-multi-container-app#create-a-docker-compose-app

# Login to Azure
Connect-AzAccount

# Create gglgroup(ResourceGroup) at japaneast(Location)
New-AzResourceGroup -Name gglgroup -Location japaneast

# Create gglcr(ContainerRegistry) with basic(Tier) in gglgroup
New-AzContainerRegistry -ResourceGroupName gglgroup -Name gglcr -Sku "Basic" -EnableAdminUser

# Create gglappserviceplan(AppServicePlan) with basic(Tier), linux(OS) at japaneast(Location)
New-AzAppServicePlan -ResourceGroupName gglgroup -Name gglappserviceplan -Location japaneast -Tier "Basic" -Linux

# Use docker to push local docker images, 
#docker tag <front>gglcr.azurecr.io/<front>
#docker tag <api> gglcr.azurecr.io/<api>
## step 1 :set variable $password with password for gglcr
$password = (Get-AzContainerRegistryCredential -ResourceGroupName gglgroup -Name gglcr).password
## step 2 : login to gglcr
az acr login --name gglcr --username gglcr --password $password
## step 3 : push <api> and <front> to gglcr
docker push gglcr.azurecr.io/<front>
docker push gglcr.azurecr.io/<api>

# Create gglapp-azure(Web App) with gglappserviceplan(AppServicePlane) in gglgroup(ResourceGroup) using <compose-azure.yml>(ComposeFile)
az webapp create --resource-group gglgroup --plan gglappserviceplan --name <app-name> --multicontainer-config-type compose --multicontainer-config-file <compose-azure.yml>

# Assign managed service identity [system] to gglapp-azure(Web App).
az webapp identity assign --resource-group gglgroup --name <app-name>

## Retrieve <app-name> as $app(Variable)
$app = Get-AzWebApp -name <app-name>
## Retrieve subscription-id as $subscriptionid(Variable) $subscriptionid = (Get-AzSubscription).id # Grant [system](the managed identity) permission to access the gglcr(Container Registry) az role assignment create --assignee $app.identity.principalid --scope /subscriptions/$subscriptionid/resourceGroups/gglgroup/providers/Microsoft.ContainerRegistry/registries/gglcr --role "AcrPull" # Configure <app-name>(Web App) to use [system](managed identity) to pull from gglcr(ContainerRegistry). az resource update --ids /subscriptions/$subscriptionid/resourceGroups/gglgroup/providers/Microsoft.Web/sites/<app-name>/config/web --set properties.acrUseManagedIdentityCreds=True #Remove-AzResourceGroup -Name gglgroup -Force
<compose-azure.yml> 的內容:
version: '3.7'
services:
  <api>:
    image: gglcr.azurecr.io/<api>
    restart: always
  <front>:
    depends_on:
       - <api>
    image: gglcr.azurecr.io/<front>
    ports:
      - 80:80
    restart: always

End 

2016年6月5日 星期日

moon phase 月相

月相

中央氣象局月相圖,我們可以查到每一天的月相資料, 舉凡新月、滿月、上弦月、下弦月等的日子,一目了然。

新月前後三四天,只有10%左右的部分是亮的,亮的部分在右側?左側?右上角?右下角?左上角?左下角?事實上,同一天的不同時刻看,亮的位置是會改變的,前述網站沒有呈現這個現象。

基本上,月亮的形狀主要由地球、月亮、太陽的相關位置以及觀察地點所決定。月亮面對太陽的半個球是亮的,面對地球的半個球是我們(地表觀察者)可以看到的,兩者的交集就是我們看到的部分。至於哪一個角落是亮的,因為地球的自轉,觀察者的天頂方向會隨時間改變,也就是視覺上的上方會隨時間改變,造成視覺上亮的部位改變。

在地平坐標系統下,只要有太陽與月亮的位置資料,我們就可以算出月相。

下面的圖形是用附錄中的程式製作出來的。它是2016年6月3日(陰曆4月28)每一整點在台北可以看到的月相。圖中的月形,藍色代表在地平面上,紅色代表在地平面下,可以明顯看到月相隨間改變的情形。

下面的圖形是2016年6月7日(陰曆5月初三)每一整點在台北可以看到的月相。

地平坐標系統

地平坐標系統以觀察地為原點,觀測目標的位置用距離(\(\rho\))、仰角(\(\phi\))、方位角(\(\theta\))來定位,這是球體坐標。距離指的是觀測站到目標的距離,仰角指的是觀測目標的方向與地面的夾角。方位角指的觀測目標的東西南北方位,以北方為基準,向東轉的角度就是。換句話說,北方的方位角為0度,東方為90度,南方為180度,西方為270度。

將東方、北方、天頂方向分別訂為直角座標系統下的X、Y、Z軸的方向,\((x,y,z)\)為目標的直角座標。直角坐標\((x,y,z)\)與球體坐標\((\rho,\theta,\phi)\)的關係如下: \[x=\rho\cos\phi\sin\theta, y=\rho\cos\phi\cos\theta, z=\sin\phi\]

月球上的臨時座標系統

月球的方位角用\(\theta\)表示、仰角用\(\phi\)表示,則地面望向月亮的方向\(e_2=(\cos\phi\sin\theta,\cos\phi\cos\theta,\sin\phi)\)。從月球位置出發,沿著同一方位角的大圓,往天頂的方向\(e_3\)就是\(e_2\)對\(\phi\)的微分, 也就是\(e_3=(-\sin\phi\sin\theta,-\sin\phi\cos\theta,\cos\phi)\)。令\(e_1=e_2\times e_3\), 從地表往月亮看,\(e_1\)的方向是月亮的右邊,\(e_3\)的方向是月亮的上邊,\(e_2\)的方向是月亮的後邊。以月亮中心為原點,\(\{e_1,e_2,e_3\}\)為坐標軸方向,我們建立一個臨時坐標系統。

令矩陣\(M\)的第一列為\(e_1\),第二列為\(e_2\),第三列為\(e_3\)。在地平坐標系統下,月球到太陽的方向向量坐標用\(S_e\)表示, 則在臨時坐標系統中,月球到太陽的方向向量坐標為\(S_m=M S_e\)。另一方面,在臨時坐標系統中,月球望向地球的方向向量為\((0,-1,0)\)。

在臨時坐標系統中,以月亮的半徑為單位,面向我們的半個月球以大圓\(C_1: x^2+z^2=1, z=0\)為邊界,\(C_1\) 的參數式為\((\cos\theta,0,\sin\theta)\)。

面向太陽的半個月亮的邊界\(C_2\)也是一個大圓,\(C_2\)就是過圓點且與\(S_m\) 垂直的平面\(E\) 跟單位球面相交的部分。令\(B_0=S_m\times (0,1,0)\),\(B_1\)是與\(B_0\)同向的單位向量,\(B_2=S_m\times B_1\)。 如此,\(B_1,B_2\)是平面\(E\)上兩個互相垂直的單位向量,而 \(C_2\)的參數式為\(\cos\theta\cdot B_1+\sin\theta\cdot B_2\)。

\(C_1,C_2\)兩大圓相交,互相切割,得到4段圓弧,屬於\(C_1\)的兩段用\(C_{11},C_{12}\)表示,屬於\(C_2\)的兩段用\(C_{21},C_{22}\)表示。從\(C_{11},C_{12}\)中挑出靠近太陽的一段(與\(S_m\)的內積大於0),從\(C_{21},C_{22}\)挑出靠近地球的一段(第二個坐標小於0),所得兩段圓弧圍得月球表面的部分是月亮中亮的部分。這兩段圓弧在臨時坐標系統中\(XZ\)平面的垂直投影,所圍成的區域就是視覺上的月相。

附錄中,利用python平台的套件ephem與matplotlib,我們製作函數 moonshape_t(lon,lat,tz,year,month,day,hour)來顯示月相。參數lon為經度,東經為正;lat為緯度,北緯為正,tz為時區(以小時為單位,台北為+8),year為西元,hour採24小時制,可以有小數。 執行moonshape_t('121.537','25.017',8,2016,6,3,16.5),我們就可得到,2016年6月3日下午4點半在台北(東經121.537度, 北緯25.017度)所看到的月相。

附錄

import ephem
import math
import numpy as np
import matplotlib.pyplot as plt

def spher2cart(theta,phi):
  return np.array([math.cos(phi)*math.sin(theta),math.cos(phi)*math.cos(theta),math.sin(phi)])

def spher2cartDphi(theta,phi):
  return np.array([-math.sin(phi)*math.sin(theta),-math.sin(phi)*math.cos(theta),math.cos(phi)])


def greatCircleWithPar(N):
  # [0,1.0,0] 為觀察者望向月球的方向
  # N 為月球望向太陽的方向
  # 通過月球球心與N垂直的平面切割月球的大圓C,C可以用cos(theta)*b1+sin(theta)*b2表示,
  # 其中的b1,b2是C平面上兩個互相垂直的單位向量
  b1=np.cross(N,np.array([0,1.0,0]))
  if np.linalg.norm(b1)>0.000001:
    b1=1/np.linalg.norm(b1)*b1
    b2=np.cross(N,b1)
  else:
    b1=np.array([1.,0,0])
    b2=np.array([0,0,1.])
  #print([b1,b2])
  def fun(theta):
    return math.cos(theta)*b1+math.sin(theta)*b2
  return fun

def cutarc(points):
  arcs=[]
  arc=[]
  for i in range(len(points)):
    if arc==[]:
      arc.append(points[i])
    else:
      if np.linalg.norm(points[i]-arc[-1])<0.1:
        arc.append(points[i])
      else:
        arcs.append(arc)
        arc=[points[i]]
  arcs.append(arc)  
  return arcs    


def moonshape_t(lon,lat,tz,year,month,day,hour,show=True):
  date=ephem.Date((year,month,day,hour-tz)) # GMT+8
  site=ephem.Observer()
  site.date=date
  site.lon=lon
  site.lat=lat
  sun=ephem.Sun(site)
  moon=ephem.Moon(site)
  # e2 望向月亮的方向
  e2=spher2cart(moon.az,moon.alt)
  # e3 月亮在天球上的往上方向
  e3=spher2cartDphi(moon.az,moon.alt)
  # e1 月亮在天球上的往右方向
  e1=np.cross(e2,e3) 
  M=np.stack([e1,e2,e3])
  moon2sun=sun.earth_distance*spher2cart(sun.az,sun.alt)-moon.earth_distance*spher2cart(moon.az,moon.alt)
  s_e=1/np.linalg.norm(moon2sun)*moon2sun
  # s_m: 以 {e1,e2,e3} 為坐標軸方向,月亮為原點時,太陽的方向
  s_m=M.dot(s_e)
  if moon.alt>0:
    color='b'
  else:
    color='r'
  print(['moon.alt',moon.alt,'moon.az',moon.az])
  f=greatCircleWithPar(s_m)
  thetas=np.linspace(-math.pi,math.pi,num=200)
  plt.plot([math.cos(theta) for theta in thetas],[math.sin(theta) for theta in thetas],'w-')
  points=[f(theta) for theta in thetas if f(theta)[1]<0]
  arcs=cutarc(points)
  for arc in arcs:
    plt.plot([x[0] for x in arc],[x[2] for x in arc],color+'-')
  points=[np.array([math.cos(theta),0.0,math.sin(theta)]) for theta in thetas 
    if np.dot(np.array([math.cos(theta),0.0,math.sin(theta)]),s_m)>0]
  arcs=cutarc(points)
  for arc in arcs:
    plt.plot([x[0] for x in arc],[x[2] for x in arc],color+'-')
  plt.axis('off')
  plt.axis('equal')
  plt.title(str(year)+'/'+str(month)+'/'+str(day)+'/'+str(hour))
  if show:
    plt.show()

lon='121.537'
lat='25.017'
tz=8
moonshape_t(lon,lat,tz,2016,6,3,16)
for i in range(1,25):
  plt.subplot(6,4,i)
  moonshape_t(lon,lat,tz,2016,6,3,i,False)

plt.show()
      

2016年5月21日 星期六

lunar calendar 農曆

計算農曆

只要知道太陽與月亮,在地心黃道座標系統上,隨時間改變的位置,我們就可以據以訂出農曆。

下面我們將用R平台上套件astrolibR來算出農曆。

農曆是甚麼?

我國的農曆基本上是陰陽合曆。陰曆的月以月亮為基礎,新月的時候就是初一。太陽與月亮落在同一黃經的那一天就是新月日,那天就是陰曆初一。一個月有時29天,有時30天,完全決定於兩次新月日之間相差29天還是30天。

農曆的年以太陽為基礎,地球繞太陽一圈需時365.2422天。用地心赤道坐標系統來講,太陽在春分與秋分兩次落在赤道平面上。一次在3月份另一次在9月份,3月份的那一刻叫春分,9月份的那一刻叫秋分。太陽在黃道面上,一年繞行地球一圈,以春分時太陽的位置為基準,每繞行15度得一節氣,分別是春分,清明,穀雨,立夏,小滿,芒種,夏至,小暑,大暑,立秋,處暑,白露,秋分,寒露,霜降,立冬,小雪,大雪,冬至,小寒,大寒,立春,雨水,驚蟄。太陽位於黃經0度時是春分,15度時是清明,餘此類推。位於黃經30度整數倍的節氣叫做中氣,換句話說,春分,穀雨,小滿,夏至,大暑,處暑,秋分,霜降,小雪,冬至,大寒,雨水等為中氣。節氣與太陽的位置關係密切,太陽是地球能量的主要來源,所謂春耕、夏耘、秋收、冬藏道盡了農作與太陽之間的關係。先人的對於農時的經驗,大致用節氣來表示。

節氣

中央氣象局的 天文常識系列 提到節氣

節氣的定法有兩種,古時候使用的稱為平氣法,就是把冬至到下一個冬至平分為24等分,每一節氣都是15天多。從清初時憲曆(西元1645年)起,節氣的推算改為定氣法,自春分點開始,太陽在黃道上每視行15度定一個節氣,節氣間隔的時間並不等長。

用astrolibR套件的sumpos,我們可以得到太陽隨時間改變的位置。其中的成分$longmed就是黃經,看哪一天太陽的位置通過黃經0度,那一天就是春分;看哪一天太陽的位置通過黃經15度,那一天就是清明,餘此類推。

在附錄中,我們自製了函數jieqi(year),用jieqi(2016)就可算出西元2016年的節氣。

陰曆初一

用astrolibR套件的moonpos,我們可以得到月亮隨時間改變的位置。計算什麼時刻,太陽與月亮的黃經相同,那刻所在的日子就是陰曆初一。

在附錄中,我們自製了函數lunardayone(year),用lunardayone(2016)就可算出西元2016年的那些天是陰曆初一。

陰曆的月份

平均起來一個陰曆月約有29.5天,而一年有365.2422天,12個陰曆月比一年短少約11天。為了維持一年有12個月份,我國用閏月的方式來解決。大致上,每隔約三年,設置一個月為閏月,其月份與前一個月同。假如前一個月是8月而這個月是閏月,這個月叫潤8月,而下個月叫9月。

哪一個月的初一是春節?哪一個月要閏月?

中央研究院劉智漢在 這裡提到歲首的問題:

自漢武帝太初元年(西元前104年)改曆以來,至今大都採用夏代的建寅制,即自冬至所在 月份起算的第三個月為正月
因此,冬至所在的陰曆月為11月。

Aslaksen的 文章列出了安置月份與置潤的原則。條列如下:

  • 計算時間時以東經120度為基準
  • 每天的起算時間為午夜零時
  • 新月那天為陰曆初一
  • 一歲的開始為冬至的那一刻所在的日子,以下一個冬至日的前一天為最終日。用歲來稱呼,與大年初一開始的年有所區別。冬至日的陰曆月訂為11月
  • 若一歲裡包含了完整的12個陰曆月,這一歲叫潤歲。在潤歲裡,第一個沒有中氣的月份必須閏月

根據這些原則,在附錄中自製了函數lunarCalendar(year)。用lunarCalendar(2017),我們算出2017年陰陽曆的對應,得到2017年潤6月,與中央氣象局的 資料 相符合。

附錄

library(astrolibR)
# 清明時太陽位於黃經15度,穀雨30度,...
jieqinames<-c("清明","穀雨","立夏","小滿","芒種","夏至","小暑","大暑","立秋"
              ,"處暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至","小寒","大寒","立春","雨水","驚蟄","春分")
# d1,d2 angle with unit degree.
diff<-function(d1,d2,mod){
  dif<- ((d1-d2) %% mod)
  if (dif>mod/2)
    dif<-dif-mod
  dif
}
# 節氣足碼,清明1,穀雨2,...,不是節氣0
jieqiindex<-function(year,month,day){
  jd<-jdcnv(year,month,day,0)-1/3
  sun1<-sunpos(jd)$longmed%%15
  sun2<-sunpos(jd+1)$longmed%%15
  if (diff(sun1,0,15)<=0 && diff(sun2,0,15)>0)
    floor(sunpos(jd)$longmed%%360/15)+1
  else
    0
}
# 是否陰曆初一
isLunarOne<-function(year,month,day){
  jd<-jdcnv(year,month,day,0)-1/3
  sun1<-sunpos(jd)$longmed%%360
  sun2<-sunpos(jd+1)$longmed%%360
  mon1<-moonpos(jd)$geolong%%360
  mon2<-moonpos(jd+1)$geolong%%360
  if (diff(sun1,mon1,360)>=0 && diff(sun2,mon2,360)<0)
    TRUE
  else
    FALSE
}

# 算出節氣的日子
jieqi<-function(year){
  leapyear=FALSE
  if (year %% 4 ==0) leapyear=TRUE
  if (year %% 100==0) leapyear=FALSE
  if (year %% 400==0) leapyear=TRUE
  days=c(31,28,31,30,31,30,31,31,30,31,30,31)
  if (leapyear) days[2]=29
  res<-list()
  for (month in seq(1,12)){
    for (day in 1:days[month]){
      index<-jieqiindex(year,month,day)
      if (index>0){
        res<-c(res,list(c(year,month,day,jdcnv(year,month,day,12),index)))
      }
    }
  }
  res
}


lunardayone<-function(year){
  leapyear=FALSE
  if (year %% 4 ==0) leapyear=TRUE
  if (year %% 100==0) leapyear=FALSE
  if (year %% 400==0) leapyear=TRUE
  days=c(31,28,31,30,31,30,31,31,30,31,30,31)
  if (leapyear) days[2]=29
  res<-list()
  for (month in seq(1,12)){
    for (day in 1:days[month]){
      if (isLunarOne(year,month,day)){
        res<-c(res,list(c(year,month,day,jdcnv(year,month,day,12))))
      }
    }
  }
  res
}

# 一歲裡陰的陰曆初一清單,假如一歲裡包含完整的12個陰曆月,則長度為13
suiones<-function(lunarones,sols1,sols2){
  res<-list()
  for (ele in lunarones){
    if (ele[4]>=sols1[4] && ele[4]<=sols2[4]){   # should be <=
      res<-c(res,list(ele))
    }
  }
  res
}
# 連續兩個陰曆初一間是否缺少中氣,及m1的月份是否缺少中氣
lackzhongqi<-function(m1,m2,jieqis){
  res<-TRUE
  for (i in seq_along(jieqis)){
    if (i%%2==0 && jieqis[[i]][4]>=m1[4] &&jieqis[[i]][4]<m2[4]){
      res<-FALSE
      break
    }
  }
  res
}

# 排定陰曆月份,包含閏月的設定
assignmonth<-function(sui,jieqis){
  leapsui<-FALSE
  if (length(sui)>=13){
    leapsui<-TRUE
  }
  completed<-FALSE
  suiclone<-sui
  Mn<-11
  for (i in seq_along(suiclone)){
    if (leapsui && !completed && lackzhongqi(suiclone[[i]],suiclone[[i+1]],jieqis)){
      suiclone[[i]]<-c(suiclone[[i]],Mn+0.5)    # 閏月
      completed<-TRUE
    }
    else {
      Mn<-(Mn+1)
      if (Mn>12) Mn<-Mn%%12
      suiclone[[i]]<-c(suiclone[[i]],Mn)   # 平月/普通月
    }
  }
  suiclone
}
lunarCalendar<-function(year){
  jieqis<-c(jieqi(year-1),jieqi(year),jieqi(year+1))
  lunarones<-c(lunardayone(year-1),lunardayone(year),lunardayone(year+1))
  wintersolstices<-list()
  for (ele in jieqis){
    #if (isSolstice(ele[1],ele[2],ele[3]))
    if (ele[5]==18)
      wintersolstices<-c(wintersolstices,list(ele))
  }
  firstsui<-suiones(lunarones,wintersolstices[[1]],wintersolstices[[2]])
  secondsui<-suiones(lunarones,wintersolstices[[2]],wintersolstices[[3]])
  firstsui<-assignmonth(firstsui,jieqis)
  secondsui<-assignmonth(secondsui,jieqis)
  res<-c()
  for (ele in c(firstsui,secondsui)){
    if (ele[1]==year){
      st=""
      if (ele[5]%%1>0) st<-"潤"   # ele[5]有小數部分代表閏月
      st<-paste0(st,floor(ele[5]),"月")
      res<-c(res,paste(ele[1],ele[2],ele[3],st))
    }
  }
  res
}

# test jieqi
res<-jieqi(2017)  
for (ele in res){
  print(paste(ele[1],ele[2],ele[3],jieqinames[ele[5]]))
}

# test lunardayone
lunardayone(2016)

# test lunarCalendar
lunarCalendar(2016)
lunarCalendar(2017)
lunarCalendar(2033)

2016年5月18日 星期三

Sunset and Sunrise 日出與日落

介紹

中央氣象局,我們可以查到台灣地區每天日出、日落的時刻。 在timeanddate網站則可查到世界各地的資料。這些資料是職業天文工作者,利用觀察資料配合模型計算出的。
我們用地心赤道坐標系統、太陽軌跡、地球自轉等資料、觀察地A的經緯度及時區等資料,估算A地點的整年日出日落時間。其中,太陽的軌跡與地球自轉的資料,取自運算平台 R 的套件astrolibR。用附錄中的程式,我們算出台北與布宜諾斯艾利斯2016年每月1日的日出日落時分,與網站上的資料相比,相差全在1分鐘之內。

模型與坐標

地球公轉的平面叫做黃道面,地球赤道所在的平面叫做赤道面,黃道面與赤道面的交角\(\epsilon\)就是所謂的地軸傾角。兩面的交線與交角都會隨時間改變。 參考下面動畫,目前地軸傾角約為23.44度。

我們採用所謂的 地心赤道坐標系統 ,將地球中心固定放在原點,赤道平面就是XY平面,地心往北極方向為Z軸,地球以Z軸正向為軸自轉。右手握拳拇指伸出,拇指方向為Z軸時,四指的方向是自轉的方向。請參考下面的動畫

太陽軌道所在的平面叫做黃道面,黃道面與赤道面的交角23.44度,就是地軸傾角\(\epsilon\)。一年之中太陽有兩次運行到赤道面上,分別是春分與秋分。春分之前,太陽在赤道面的下方,之後在赤道面的上方。在春分或秋分時刻,地球南北半球各洽有一半是白天,一半是夜晚。春分之後太陽照到北半球的部分開始大於南半球,並且差距越來越大,直到夏至。此時,太陽在離赤道面最高的地方。夏至後,差距開始縮小,直到秋分,太陽照到地球南北半球的部分再度相等。秋分前後,太陽由赤道面的上方跑到赤道面的下方,秋分之後太陽照到北半球的部分開始小於南半球,並且差距越來越大,直到冬至。此時,太陽在赤道面下方離赤道面最遠的地方。
我們將春分那一瞬間,地球指向太陽的方向為X軸,以地球到太陽的距離為單位,將當時太陽的坐標定為(1,0,0), 地球的半徑用R表示。有了Z軸與X軸,Y軸方向的訂定如一般右手系的直角坐標系統。地球的半徑用R表示。
在地心赤道坐標系統中,假設物體的直角座標為\((x,y,z)\),該物體在XY平面上投影的幅角叫做赤經\(\alpha\),\((x,y,z)\)與XY平面的夾角叫做赤緯\(\delta\), \((x,y,z)\)的長度用\(\rho\)表示,則有下列關係: \[x=\rho \cos\delta \cos\alpha, y=\rho \cos\delta \sin\alpha, z=\rho \sin\delta\]

Julian 日數(JD)

太陽的軌跡以及地球自轉的狀態,需要與時間建立關係。在天文學上,用Julian日數來代表時間。所謂Julian日數(JD),是以西元前4713年1月1日中午的時刻起算的日數。西元前4713年1月1日12:00的JD=0,西元前4712年1月1日00:00的JD=-0.5。astrolibR套件中的函數jdcnv(year,month,day,hour)可用來算JD。例如,jdcnv(1,2,3,4.1)=1721458.671 代表西元1年2月3日4:06的JD為1721458.671。注意,jdcnv不能用來算西元前的JD。

地面上定點的赤經

A地的經緯度為\(E_A\) 經 \(N_A\) 緯,地球自轉一圈時,A地也繞圓圈轉一圈,圓圈的半徑是\(R \cos N_A\)。 這裡,有一點要注意,地球並不是一天自轉一圈。地球轉動,同一經線所在半平面,連續兩次掃過太陽的時間間隔才是一天。由於太陽一天繞行地球1/365.2425圈,所以地球每天要轉動比一圈多一點點的366.2425/365.2425圈,才能讓同一經線半平面再次掃過太陽。換句話說,地球一年之中自轉了366.2425圈。令\(\theta_A\)代表A地的赤經,則A地的直角坐標\(\vec{A}\)為\((R \cos N_A\cos \theta_A,R\cos N_A\sin\theta_A,R\sin N_A)\)。
\(\theta_A\)隨時間改變,我們可以用astrolibR套件中的函數ct2lst(long,tz,jd)計算之,函數值以時角為單位。這裡的時角是角度單位而非時間單位,1時角度相當於15度。1時角,細分為60分,每分又細分為60秒。例如,ct2lst(0,0,jdcnv(2016,3,20,4.5))=16.3761,代表格林威治時間2016年3月20日4:30, 倫敦(東經0度,時區GMT+0)的赤經為16.3761時角。ct2lst(121.537,8,jdcnv(2016,3,20,4.5)) =0.4786,代表格林威治時間2016年3月20日4:30, 台北(東經121.537度,時區GMT+8)的赤經為0.4786時角。

太陽的仰角與昇落方程式

A地的地平面向上單位法向量\(\vec{n}\)為\(\frac{1}{R}\vec{A}\)。用\(\vec{S}=(s_x,s_y,s_z)\)代表太陽的坐標, \(\vec{S}-\vec{A}\)就是從A地觀察太陽的方向。內積\(\vec{n}\cdot(\vec{S}-\vec{A})\)可用來判定太陽與地平面的相關位置。內積大於0時,代表太陽在地平面上方,是白天;小於0代表太陽在地平面下方,是夜晚; 等於0代表洽在地平面,也就是日出日落時分。因此,A地日出日落時刻滿足方程式 \(\vec{n}\cdot(\vec{S}-\vec{A})=0\)。也就是\(\vec{n}\cdot\vec{S}=R\), R的值4.25875e-05很小,用0來近似,變成\(\vec{n}\cdot\vec{S}=0\),其中\(\vec{n}\cdot\vec{S}\)是太陽中心位置與天頂方向的夾角餘弦,夾角餘弦等於0代表太陽中心落在地平線。因為太陽上緣露出地平就算日出,以至於太陽中心仍在地平下方就已看到太陽。又因為光線進入大氣層會發生折射,即使太陽上縁略低於地平就可看見陽光。根據 維基百科,太陽圓盤視角為32分。一般認為,折射影響的角度有34分,再加上太陽的圓盤又貢獻了16分,太陽中心在地平下方角度50分時為日出日落時刻,也就是\(\vec{n}\cdot\vec{S}=\cos(90^{\circ}50')\)。所以,日出日落時刻是解下列方程式(簡稱昇落方程式) \[s_x\cos N_A\cos \theta_A+s_y\cos N_A\sin\theta_A+s_z\sin N_A +\sin(50')=0\] 要用上述方程式求解日出日落時刻,我們已知\(\theta_A\)隨時間的變化,還需要知道太陽隨時間變化的位置。

太陽的軌跡

太陽的位置可用astrolibR套件中的函數sunpos(jd)求得。引數jd是JD,函數值的成分$ra,$dec分別是太陽位置的赤經、赤緯。例如,令s等於sunpos(jdcnv(2016,5,19,12)),則s$ra是格林威治時間2016年5月19日中午12點的時候太陽位置的赤經而s$dec是赤緯,單位是度。

台北的日出日落

將太陽的赤經緯以及台北的緯度及赤經資料,代入昇落方程式求解,解出來的JD就是台北的日出日落時間。數值計算上,我們只要計算一天之中,每一分鐘台北昇落方程式左邊的數值,前後兩分鐘數值由負變正,就找到日出時刻。前後兩分鐘數值由正變負,就找到日落時刻。不過這個時間是格林威治時間,換算成台北時區的時間還要再加上8小時。
附錄中的函數sunriseset(E,N,timezone,month,day),其函數值就是日出日落的時刻。所需引數,E是觀察地點的經度,東經為正,西經為負,N是緯度,北緯為正,南緯為負,兩者都是度度量。timezone是觀察地的時區,以小時為單位,台北時區為GMT+8,所以台北的timezone為8,布宜諾斯艾利斯的時區為GMT-4,但因實施夏令時間,布宜諾斯艾利斯的timezone為-3。由 latlong 查出城市的經緯度,由 timeanddate 查出時區,就可以用函數sunriseset算出2016年month月份day日的日出日落時刻。
用sunriseset(E,N,timezone,month,day)函數算出台北2016年每月1日的日出日落時間如下表,日出/日落欄位是sunriseset函數計算所得, sunrise/sunset欄位取自 中央氣象局 。表中,可以看出sunriseset的計算與中央氣象局的資料最多相差1分。
日出 sunrise 日落 sunset
1 1 06:39 06:39 17:15 17:15
2 1 06:37 06:37 17:38 17:38
3 1 06:17 06:17 17:56 17:56
4 1 05:45 05:46 18:10 18:10
5 1 05:18 05:18 18:24 18:24
6 1 05:04 05:04 18:40 18:40
7 1 05:08 05:08 18:48 18:48
8 1 05:21 05:21 18:39 18:39
9 1 05:35 05:35 18:13 18:13
10 1 05:46 05:46 17:41 17:41
11 1 06:01 06:01 17:13 17:13
12 1 06:22 06:22 17:04 17:04

布宜諾斯艾利斯的日出日落

接著用sunriseset(E,N,timezone,month,day)函數算出布宜諾斯艾利斯2016年每月1日的日出日落時間如下表,日出/日落欄位是sunriseset函數計算所得, sunrise/sunset欄位取自 timeanddate 。表中,可以看出sunriseset的計算與timeanddate網站的資料,完全相同。
日出 sunrise 日落 sunset
1 1 05:44 05:44 20:10 20:10
2 1 06:13 06:13 20:00 20:00
3 1 06:41 06:41 19:30 19:30
4 1 07:06 07:06 18:47 18:47
5 1 07:30 07:30 18:11 18:11
6 1 07:52 07:52 17:51 17:51
7 1 08:01 08:01 17:54 17:54
8 1 07:47 07:47 18:13 18:13
9 1 07:12 07:12 18:35 18:35
10 1 06:30 06:30 18:57 18:57
11 1 05:51 05:51 19:23 19:23
12 1 05:34 05:34 19:52 19:52

附錄


options(digits=14)
library(astrolibR)
Rad<-pi/180

N.taipei<-25.017    
E.taipei<-121.537   
timezone.taipei<- 8
correctionangle<- -50/60

sunheight<-function(t,E,N,timezone){
  jd<-t-timezone/24
  sun<-sunpos(jd)
  sx<-cos(sun$dec*Rad)*cos(sun$ra*Rad)
  sy<-cos(sun$dec*Rad)*sin(sun$ra*Rad)
  sz<-sin(sun$dec*Rad)

  theta<-ct2lst(E,timezone,jd)*pi/12
  sx*cos(N*Rad)*cos(theta)+
  sy*cos(N*Rad)*sin(theta)+
  sz*sin(N*Rad)-sin(correctionangle*Rad)
}

timestring<-function(i){
  min<-i%%60
  hour<-(i-min)/60
  sprintf("%02d:%02d",hour,min)
}

sunriseset<-function(E,N,timezone,month,day){
  minutes<-seq(1,1440)/1440
  day<-jdcnv(2016,month,day,0)
  v1<-sunheight(day+minutes[1],E,N,timezone)
  sunset<-"-"
  sunrise<-"-"
  for (i in 1:1439){
    v2<-sunheight(day+minutes[i+1],E,N,timezone)
    product<-v1*v2
    if (product<0){
      if (abs(v1)<abs(v2))
        j<-i
      else
        j<-i+1
      if (v1<0)
        sunrise<-timestring(j)
      else
        sunset<-timestring(j)
    } else if (product==0) {
      if (v1==0) {
        if (v2>0)
          sunrise<-timestring(i)
        else
          sunset<-timestring(i)
      } else {
        if (v1>0)
          sunset<-timestring(i+1)
        else
          sunrise<-timestring(i+1)
      }
    }
    v1<-v2
  }
  paste0(sunrise,"\t",sunset)
}

msg<-""
for (month in 1:12){
  day<-1
    msg<-paste0(msg,month,'\t',day,'\t',sunriseset(E.taipei,N.taipei,timezone.taipei,month,day),'\n')
}
cat(msg)

E.buenosaires<--58.381559
N.buenosaires<--34.603684
timezone.buenosaires<--3

msg<-""
for (month in 1:12){
  day<-1
    msg<-paste0(msg,month,'\t',day,'\t',sunriseset(E.buenosaires,N.buenosaires,timezone.buenosaires,month,day),'\n')
}
cat(msg)
      

2016年4月26日 星期二

圓柱與圓錐 Cylinder and cone

火車輪子的幾何奧秘

火車行駛在兩條平行的鐵軌上,車輪順著軌道走。鐵軌轉彎,火車跟著轉彎,看起來很自然。我們來了解它的運作機制。

初看起來,輪子像個圓柱面,內側輪緣凸起防止火車出軌。請見摘自 commons.wikimedia.org的附圖 ,左右兩輪以剛性的輪軸相連,因此兩輪的轉速相同,左輪轉一圈右輪就轉一圈。

其實,正確的講,輪子與鐵軌接觸的部分不是圓柱面而是圓錐面。下圖摘自 維基百科,畫出細節,藍色部分是左右兩鐵輪,紅色部分為左右兩條鐵軌,輪子與鐵軌接觸的部分是圓錐面,內側部分的半徑較大。

軌道筆直前進的情況

當兩條軌道筆直前進時,火車筆直前進,左右兩輪轉動的距離相同,這代表圓錐輪面與鐵軌接觸部分的半經相同,也意味著左右兩輪的輪緣與鐵軌之間的間隙相同,輪緣與鐵軌沒有直接接觸,輪緣不會磨損。

軌道往左彎的情況

當鐵軌往左轉彎時,火車跟著往左轉彎。由於離心力的緣故,車輪會往右偏移一些,見下圖,右輪輪緣與鐵軌的間隙變小, 而左輪輪緣與鐵軌的間隙變大。因此,右輪的接觸面半徑變大,左輪的接觸面半徑變小。由於右輪的接觸面半徑大於左輪, 右輪的轉動距離大於左輪,與鐵軌往左彎的時候右邊鐵軌比左邊鐵軌長互相符合。只要鐵軌轉彎的幅度不太大, 火車的速度不太快,輪緣與鐵軌就可以維持有稍許的間隙。

後續

對輪子與鐵軌的耗損感興趣嗎?可以參考這篇文章

2016年4月12日 星期二

偏差樣本的相關係數

高中成績比指考成績更適合大學選才?

有些大學,分析自己學校學生的大學成績、高中成績、指考成績,計算之間的相關係數。發現高中成績與大學成績間的相關係數略高於指考成績與大學成績間的相關係數。發生質疑,是否高中成績比指考成績更適合當作大學選才的工具?

事實上,這樣的論證建立在偏差樣本之上,不具說服力。請看以下的逐步說明。

指考成績的效度

一般認為:若指考成績愈高的同學,大學成績也愈高,也就是指考成績與大學成績高度正相關,則用指考成績來挑選學生是恰當的。反之,若指考成績與大學成績相關係數不高,則用指考成績來挑選學生是不恰當的。

換句話說,以大學成績為效標,指考成績與大學成績的相關係數就是指考成績的效度,指考的效度愈高,用它來挑選進大學的學生愈恰當。

假設高中在校成績與大學成績間的相關係數大於指考成績與大學成績間的相關係數,也就是說,高中在校成績的效度大於指考成績的效度。我們會質疑:為什麼不用高中成績取代指考成績,當作大學入學選才的篩選工具。

大學要挑選學生,待挑選的學生是所有的高中畢業生;所有有意願進大學的學生;所有參加指考的學生。要算指考效度,所面對的母體是所有參加指考的學生

那麼,指考成績的效度與高中成績的效度到底誰大?

相關係數(效度)的估計

隨機樣本的樣本相關係數 \(r\) 是母體相關係數 \(\rho\) 的好估計量

一般來說,我們用隨機樣本的樣本相關係數 \(r\) 來估計母體相關係數 \(\rho\) ,效果不錯。

抽取樣本時,若每一個個體被抽到的機率都一樣,所得的樣本叫做隨機樣本。隨機樣本的 \(r\) 是 \(\rho\) 的好估計量。

下面我們模擬第一組資料,母體是二變量常態 \((X,Y)\),其中 \(X,Y\) 的標準差都是 15,期望值皆為 50, 相關係數 0.7。從中抽取樣本大小 \(n=10000\) 的隨機樣本,稱作樣本甲,計算所得樣本的相關係數。

請看下面用 R 寫的程式片段

# 下面的小程式 simulate1,模擬一個二變量常態(X,Y)的隨機樣本,樣本大小為 n,
# X,Y的標準差都是 sigma,期望值皆為mu, 相關係數 rho。
simulate1<-function(rho,mu,sigma,n){
    sigma2<-sigma*sigma
    data.frame(mvrnorm(n,c(mu,mu),matrix(c(sigma2,rho*sigma2,rho*sigma2,sigma2),ncol=2)))
}
# 用simulate1模擬rho=0.7,mu=50,sigma=15,n=10000 的隨機樣本。
options(digits=4)
library(MASS)
set.seed(123)
rho<-0.7
DataA<-simulate1(rho=rho,mu=50,sigma=15,n=10000)  #DataA 是樣本甲
colnames(DataA)<-c('X','Y')
# 計算樣本相關係數 r 用來估計 rho。
r<-cor(DataA)[1,2]
c(rho=rho,r=r)

與執行結果
   rho      r 
0.7000 0.6985

我們可以看到,用隨機樣本甲的相關係數(r=0.6985)來估計母體相關係數(rao=0.7),效果相當理想。

偏差樣本的樣本相關係數\(r\)是母體相關係數\(\rho\)的差勁估計量

抽取樣本時,若每一個個體被抽到的機率不完全一樣,所得的樣本叫做偏差樣本。偏差樣本的 \(r\)是 \(\rho\) 的差勁估計量。

偏差樣本隨處可見,沒有經過精心設計的電話民調,是一個普遍存在的偏差樣本。例如,電話調查時間,在野外工作的人不會被抽到,有多支電話的人較容易被抽到,……。

假設,\(X\) 為指考成績,\(Y\) 為大一微積分成績,用 \(X,Y\)間的相關係數 \(\rho\) 當作 \(X\) 的效度。如何用資料估計 \(\rho\) 呢?

此時的母體是所有參加指考的學生,進行隨機抽樣不可行,因為只有進大學的學生才有微積分成績 \(Y\)。假設,台大利用台大大一學生的指考成績 \(X\) 與微積分成績 \(Y\),計算 \(X,Y\) 間的相關係數 \(r\),這個 \(r\) 不適合當作指考效度 \(\rho\) 的估計值。理由是:樣本是母體的偏差樣本,沒進入台大的學生全部不在樣本裡,這個樣本不能代表所有參加指考學生的母體,這個樣本是個極度偏差的樣本。

偏差樣本影響多大?這要看偏差有多嚴重而定。下面模擬一個簡單的偏差樣本,單純的將樣本甲中\(X\) 低於 80 分的學生移除,所得樣本叫樣本乙,計算樣本乙的相關係數,其中 rho、rhohat、r 分別為母體相關係數、隨機樣本甲相關係數、偏差樣本乙相關係數。

請看下面的程式片段

# 先來看看一種極度簡化的偏差樣本。
# 將前述隨機樣本中,X<80 的學生移除
DataB<-DataA[DataA$X>=80,]    # DataB 是樣本乙
c(rho=rho,rhohat=cor(DataA)[1,2],r=cor(DataB)[1,2],SubSamplesize=length(DataB$X))

結果為
          rho        rhohat             r SubSamplesize 
       0.7000        0.6985        0.3577      220.0000

我們可以看到偏差樣本乙的相關係數 r=0.3577 與母體相關係數 \(\rho\)=0.7 相距甚遠。可見,偏差的程度還滿嚴重的。

模擬進入某校學生的偏差樣本

接著我們模擬略為逼真的考生成績,包括指考國文(Chi)、 指考英文(Eng)、 指考數學(Math)、 高中成績(GPA)、大一微積分成績(Cal)。假設母體中,這五個成績呈現多變量常態分配,每一科的平均皆為 50,標準差都是 15,任兩者間的相關係數皆為 0.7。因此,Chi, Eng, Math, GPA 對大學成績 Cal 的效度都相同。

從母體中抽出樣本數為 50000 的樣本丙。

某系A採計國文、英文、數學成績選才,三科權數皆相同,加權總分 SAT=(Chi+Eng+Math)/3。計算隨機樣本丙中,Chi, Eng, Math, GPA, Cal, SAT間的相關係數。

程式如下:
# 下面的小程式 simulate2,模擬一個五變量常態的隨機樣本,樣本大小為 samplesize,
# 母體中每個變數的標準差都是 sigma,期望值皆為mu, 任兩變數間的相關係數都是 rho。
# 變數名稱為'Chi','Eng','Math','GPA','Cal',再用'Chi','Eng','Math'加權合計得變數SAT。
simulation2<-function(satname=c('Chi','Eng','Math'),
                      weights=c(1,1,1),rho=0.7, mu=50,
                      sigma=15,sampleSize=50000){
  predicters<-c(satname,'GPA')
  # Data 為模擬的成績資料,欄位依序為'Chi','Eng','Math','GPA','Cal'
  npredicters<-length(predicters)
  K<-npredicters+1
  Mu<-rep(mu,K)
  Sigma2<-(sigma*sigma)*(matrix(rep(rho,K*K),ncol=K)+(1-rho)*diag(K))
  Data<-data.frame(mvrnorm(sampleSize,Mu,Sigma2))
  colnames(Data)<-c(predicters,'Cal')
  # 計算SAT
  Data$SAT<-0
  for (i in 1:length(satname)){
      Data$SAT<-Data$SAT+Data[[i]]*weights[i]
  }
  Data$SAT<-Data$SAT/sum(weights)
  Data
}
DataC<-simulation2(satname=c("Chi","Eng","Math"),
                  weights=c(1,1,1),rho=0.7,mu=50,
                  sigma=15,sampleSize=50000)         # DataC 為樣本丙
cor(DataC)

結果如下:
        Chi    Eng   Math    GPA    Cal    SAT
Chi  1.0000 0.7001 0.6978 0.6998 0.6981 0.8942
Eng  0.7001 1.0000 0.7002 0.7006 0.7005 0.8946
Math 0.6978 0.7002 1.0000 0.7000 0.6989 0.8938
GPA  0.6998 0.7006 0.7000 1.0000 0.6992 0.7830
Cal  0.6981 0.7005 0.6989 0.6992 1.0000 0.7819
SAT  0.8942 0.8946 0.8938 0.7830 0.7819 1.0000

我們可以看到,Chi, Eng, Math, GPA, Cal之間的樣本相關係數都很接近母體的相關係數 0.7。SAT 與這五科間的樣本相關係數都高於 0.7 而介於 0.78 到 0.90 之間。再次驗證,隨機樣本的相關係數是母體相關係數的好估計量。

因為種種原因,落點分析是原因之一,該系錄取的學生 SAT 成績都介於 70 到 80 之間。要模擬這種現象,我們簡單的把 SAT 成績低於 70 或高於 80 的移除,形成樣本丁,計算樣本丁的相關係數。

程式如下:
DataD<-DataC[DataC$SAT>=70 & DataC$SAT<=80,]         # DataD 為樣本丁
cor(DataD)

所得結果如下:
          Chi      Eng     Math     GPA     Cal    SAT
Chi   1.00000 -0.28014 -0.30888 0.06163 0.09176 0.3470
Eng  -0.28014  1.00000 -0.27839 0.11370 0.06672 0.3877
Math -0.30888 -0.27839  1.00000 0.07752 0.11559 0.3898
GPA   0.06163  0.11370  0.07752 1.00000 0.30749 0.2248
Cal   0.09176  0.06672  0.11559 0.30749 1.00000 0.2441
SAT   0.34697  0.38766  0.38979 0.22483 0.24411 1.0000

我們可以看到,微積分(Cal)與高中成績(GPA)的樣本相關(0.30749)明顯高於 Cal 與 Chi,Eng,Math 的樣本相關(介於0.07到0.12之間)。而 Cal 與 GPA 的樣本相關(0.30749)又明顯的低於母體的相關係數(0.7)。我們知道,在母體中,Chi,Eng,Math,GPA 與 Cal 的相關都是0.7。然而在偏差樣本(錄取樣本)中,Cal 與 GPA 的相關高於 Cal 與 Chi,Eng,Math 的相關。Chi,Eng,Math 與 GPA 之間的唯一差別,在於錄取時採計 Chi,Eng,Math 而沒有採計 GPA。

在我們的模擬資料中,偏差樣本中看到的現象,Cal 與 GPA 的相關高於 Cal 與 Chi,Eng,Math 的相關,是個假象,因為我們都知道,母體中的相關係數全都是 0.7。

再看另外一個例子,假設該系採計的方式改成 Chi,Eng,Math 的權數分別為 0.25,0.25,0.5,其他情況不變,得到另一個偏差的錄取樣本戊,計算樣本戊的相關係數。

程式如下:
DataE<-simulation2(satname=c("Chi","Eng","Math"),
                  weights=c(1,1,2),rho=0.7,mu=50,
                  sigma=15,sampleSize=50000)         
DataE<-DataE[DataE$SAT>=70 & DataE$SAT<=80,]         # DataE 為樣本戊
cor(DataE)

所得結果如下:
          Chi      Eng      Math       GPA       Cal    SAT
Chi   1.00000 -0.04851 -0.370287  0.168311  0.171401 0.3077
Eng  -0.04851  1.00000 -0.379517  0.179660  0.166253 0.3370
Math -0.37029 -0.37952  1.000000 -0.009813 -0.004321 0.4876
GPA   0.16831  0.17966 -0.009813  1.000000  0.302994 0.2521
Cal   0.17140  0.16625 -0.004321  0.302994  1.000000 0.2498
SAT   0.30775  0.33705  0.487645  0.252148  0.249848 1.0000

我們可以看到,Math採計權數 0.5 最高,Chi,Eng 的權數(0.25)次之,GAP(權數0)又次之,對應的 Cal 與 Math 的樣本相關最低(-0.004321),Cal 與 Chi,Eng 的樣本相關次低(0.171與0.166),Cal 與 GPA 的樣本相關最高。

總而言之,採計權重越大,錄取樣本中相關越低。

錄取樣本中,Math 與 Cal 的相關最低是由採計最重所造成,不是母體中的相關較低所造成。

結論

有些大學,分析自己學校學生的大學成績、高中成績、指考成績,計算之間的樣本相關係數。發現高中成績與大學成績間的相關係數略高於指考成績與大學成績間的相關係數,作成結論:高中成績效度比指考成績效度高。

因為用來計算的樣本是個非常偏差的樣本,偏差樣本中看到的現象,完全無法推論母體會有同樣的現象。也就是說,所得的結論,是用一個以偏蓋全的錯誤方式,得到十足具有爭議性的結論。

MongoDB Atlas

IP 地址白名單 為了安全,mongodb atlas 資料庫,除了連線時需要提供使用者名稱與密碼之外,可以設定IP地址(IP Address)白名單。只有列在白名單的電腦可以存取它。 白名單中,可以是單一IP,也可以是 CIDR  IP 區間( CIDR -notated ra...