数据分析案例-气象数据分析
目錄
實(shí)驗(yàn)背景
待檢驗(yàn)的假設(shè):靠海對(duì)氣候的影響
?實(shí)驗(yàn)過(guò)程
實(shí)驗(yàn)步驟
溫度數(shù)據(jù)分析
濕度數(shù)據(jù)分析
風(fēng)向頻率玫瑰圖
實(shí)驗(yàn)總結(jié)
實(shí)驗(yàn)背景
氣象數(shù)據(jù)是在網(wǎng)上很容易找到的一類(lèi)數(shù)據(jù)。很多網(wǎng)站都提供以往的氣壓、氣溫、濕度和降雨量等氣象數(shù)據(jù)。只需指定位置和日期,就能獲取一個(gè)氣象數(shù)據(jù)文件。這些測(cè)量數(shù)據(jù)是由氣象站收集的。氣象數(shù)據(jù)這類(lèi)數(shù)據(jù)源涵蓋的信息范圍較廣。數(shù)據(jù)分析的目的是把原始數(shù)據(jù)轉(zhuǎn)化為信息,再把信息轉(zhuǎn)化為知識(shí),因此拿氣象數(shù)據(jù)作為數(shù)據(jù)分析的對(duì)象來(lái)講解數(shù)據(jù)分析全過(guò)程再合適不過(guò)。
待檢驗(yàn)的假設(shè):靠海對(duì)氣候的影響
靠海對(duì)氣候有什么影響?這個(gè)問(wèn)題可以作為數(shù)據(jù)分析的一個(gè)不錯(cuò)的出發(fā)點(diǎn)。我不想把本章寫(xiě)成科學(xué)類(lèi)讀物,只是想借助這樣一種方式,讓數(shù)據(jù)分析愛(ài)好者能夠把所學(xué)用于實(shí)踐,解決 “海洋對(duì)一個(gè)地區(qū)的氣候有何影響” 這個(gè)問(wèn)題。
我們選取了意大利的 10 個(gè)城市。隨后將分析它們的天氣數(shù)據(jù),其中 5 個(gè)城市在距海 100 公里范圍內(nèi),其余 5 個(gè)距海 100~400 公里。
選作樣本的城市列表如下:
- Ferrara(費(fèi)拉拉)
- Torino(都靈)
- Mantova(曼托瓦)
- Milano(米蘭)
- Ravenna(拉文納)
- Asti(阿斯蒂)
- Bologna(博洛尼亞)
- Piacenza(皮亞琴察)
- Cesena(切塞納)
- Faenza(法恩莎)
?實(shí)驗(yàn)過(guò)程
導(dǎo)入相關(guān)包開(kāi)始實(shí)驗(yàn)
import numpy as np import pandas as pd import datetime如果你想用本章的數(shù)據(jù),需要加載寫(xiě)作本章時(shí)保存的 10 個(gè) CSV 文件。
df_ferrara = pd.read_csv('ferrara_270615.csv') df_milano = pd.read_csv('milano_270615.csv') df_mantova = pd.read_csv('mantova_270615.csv') df_ravenna = pd.read_csv('ravenna_270615.csv') df_torino = pd.read_csv('torino_270615.csv') df_asti = pd.read_csv('asti_270615.csv') df_bologna = pd.read_csv('bologna_270615.csv') df_piacenza = pd.read_csv('piacenza_270615.csv') df_cesena = pd.read_csv('cesena_270615.csv') df_faenza = pd.read_csv('faenza_270615.csv')我們把這些數(shù)據(jù)讀入內(nèi)存,完成了實(shí)驗(yàn)準(zhǔn)備的部分。
實(shí)驗(yàn)步驟
從數(shù)據(jù)可視化入手分析收集到的數(shù)據(jù)是常見(jiàn)的做法。前面講過(guò),matplotlib 庫(kù)提供一系列圖表生成工具,能夠以可視化形式表示數(shù)據(jù)。數(shù)據(jù)可視化在數(shù)據(jù)分析階段非常有助于發(fā)現(xiàn)研究系統(tǒng)的一些特點(diǎn)。
導(dǎo)入以下必要的庫(kù):
%matplotlib inline import matplotlib.pyplot as plt import matplotlib.dates as mdates from dateutil import parser溫度數(shù)據(jù)分析
舉例來(lái)說(shuō),非常簡(jiǎn)單的分析方法是先分析一天中氣溫的變化趨勢(shì)。我們以城市米蘭為例。
# 取出我們要分析的溫度和日期數(shù)據(jù) y1 = df_milano['temp'] x1 = df_milano['day']# 把日期數(shù)據(jù)轉(zhuǎn)換成 datetime 的格式 day_milano = [parser.parse(x) for x in x1]# 調(diào)用 subplot 函數(shù), fig 是圖像對(duì)象,ax 是坐標(biāo)軸對(duì)象 fig, ax = plt.subplots()# 調(diào)整x軸坐標(biāo)刻度,使其旋轉(zhuǎn)70度,方便查看 plt.xticks(rotation=70)# 設(shè)定時(shí)間的格式 hours = mdates.DateFormatter('%H:%M')# 設(shè)定X軸顯示的格式 ax.xaxis.set_major_formatter(hours)# 畫(huà)出圖像,day_milano是X軸數(shù)據(jù),y1是Y軸數(shù)據(jù),‘r’代表的是'red' 紅色 ax.plot(day_milano ,y1, 'r')執(zhí)行上述代碼,將得到如圖所示的圖像。由圖可見(jiàn),氣溫走勢(shì)接近正弦曲線(xiàn),從早上開(kāi)始?xì)鉁刂饾u升高,最高溫出現(xiàn)在下午兩點(diǎn)到六點(diǎn)之間,隨后氣溫逐漸下降,在第二天早上六點(diǎn)時(shí)達(dá)到最低值。?
我們進(jìn)行數(shù)據(jù)分析的目的是嘗試解釋是否能夠評(píng)估海洋是怎樣影響氣溫的,以及是否能夠影響氣溫趨勢(shì),因此我們同時(shí)來(lái)看幾個(gè)不同城市的氣溫趨勢(shì)。這是檢驗(yàn)分析方向是否正確的唯一方式。因此,我們選擇三個(gè)離海最近以及三個(gè)離海最遠(yuǎn)的城市。
# 讀取溫度和日期數(shù)據(jù) y1 = df_ravenna['temp'] x1 = df_ravenna['day'] y2 = df_faenza['temp'] x2 = df_faenza['day'] y3 = df_cesena['temp'] x3 = df_cesena['day'] y4 = df_milano['temp'] x4 = df_milano['day'] y5 = df_asti['temp'] x5 = df_asti['day'] y6 = df_torino['temp'] x6 = df_torino['day']# 把日期從 string 類(lèi)型轉(zhuǎn)化為標(biāo)準(zhǔn)的 datetime 類(lèi)型 day_ravenna = [parser.parse(x) for x in x1] day_faenza = [parser.parse(x) for x in x2] day_cesena = [parser.parse(x) for x in x3] day_milano = [parser.parse(x) for x in x4] day_asti = [parser.parse(x) for x in x5] day_torino = [parser.parse(x) for x in x6]# 調(diào)用 subplots() 函數(shù),重新定義 fig, ax 變量 fig, ax = plt.subplots() plt.xticks(rotation=70)hours = mdates.DateFormatter('%H:%M') ax.xaxis.set_major_formatter(hours)#這里需要畫(huà)出三根線(xiàn),所以需要三組參數(shù), 'g'代表'green' ax.plot(day_ravenna,y1,'r',day_faenza,y2,'r',day_cesena,y3,'r') ax.plot(day_milano,y4,'g',day_asti,y5,'g',day_torino,y6,'g')?上述代碼將生成如圖 所示的圖表。離海最近的三個(gè)城市的氣溫曲線(xiàn)使用紅色,而離海最遠(yuǎn)的三個(gè)城市的曲線(xiàn)使用綠色。結(jié)果看起來(lái)不錯(cuò)。離海最近的三個(gè)城市的最高氣溫比離海最遠(yuǎn)的三個(gè)城市低不少,而最低氣溫看起來(lái)差別較小。
我們可以沿著這個(gè)方向做深入研究,收集 10 個(gè)城市的最高溫和最低溫,用線(xiàn)性圖表示氣溫最值點(diǎn)和離海遠(yuǎn)近之間的關(guān)系。
# dist 是一個(gè)裝城市距離海邊距離的列表 dist = [df_ravenna['dist'][0],df_cesena['dist'][0],df_faenza['dist'][0],df_ferrara['dist'][0],df_bologna['dist'][0],df_mantova['dist'][0],df_piacenza['dist'][0],df_milano['dist'][0],df_asti['dist'][0],df_torino['dist'][0] ]# temp_max 是一個(gè)存放每個(gè)城市最高溫度的列表 temp_max = [df_ravenna['temp'].max(),df_cesena['temp'].max(),df_faenza['temp'].max(),df_ferrara['temp'].max(),df_bologna['temp'].max(),df_mantova['temp'].max(),df_piacenza['temp'].max(),df_milano['temp'].max(),df_asti['temp'].max(),df_torino['temp'].max() ]# temp_min 是一個(gè)存放每個(gè)城市最低溫度的列表 temp_min = [df_ravenna['temp'].min(),df_cesena['temp'].min(),df_faenza['temp'].min(),df_ferrara['temp'].min(),df_bologna['temp'].min(),df_mantova['temp'].min(),df_piacenza['temp'].min(),df_milano['temp'].min(),df_asti['temp'].min(),df_torino['temp'].min() ]先把最高溫畫(huà)出來(lái)。
fig, ax = plt.subplots() ax.plot(dist,temp_max,'ro')如圖 所示,現(xiàn)在你可以證實(shí),海洋對(duì)氣象數(shù)據(jù)具有一定程度的影響這個(gè)假設(shè)是正確的(至少這一天如此)。進(jìn)一步觀察上圖,你會(huì)發(fā)現(xiàn)海洋的影響衰減得很快,離海 60~70 公里開(kāi)外,氣溫就已攀升到高位。
用線(xiàn)性回歸算法得到兩條直線(xiàn),分別表示兩種不同的氣溫趨勢(shì),這樣做很有趣。我們可以使用 scikit-learn 庫(kù)的 SVR 方法。
from sklearn.svm import SVR# dist1是靠近海的城市集合,dist2是遠(yuǎn)離海洋的城市集合 dist1 = dist[0:5] dist2 = dist[5:10]# 改變列表的結(jié)構(gòu),dist1現(xiàn)在是5個(gè)列表的集合 # 之后我們會(huì)看到 nbumpy 中 reshape() 函數(shù)也有同樣的作用 dist1 = [[x] for x in dist1] dist2 = [[x] for x in dist2]# temp_max1 是 dist1 中城市的對(duì)應(yīng)最高溫度 temp_max1 = temp_max[0:5] # temp_max2 是 dist2 中城市的對(duì)應(yīng)最高溫度 temp_max2 = temp_max[5:10]# 我們調(diào)用SVR函數(shù),在參數(shù)中規(guī)定了使用線(xiàn)性的擬合函數(shù) # 并且把 C 設(shè)為1000來(lái)盡量擬合數(shù)據(jù)(因?yàn)椴恍枰_預(yù)測(cè)不用擔(dān)心過(guò)擬合) svr_lin1 = SVR(kernel='linear', C=1e3) svr_lin2 = SVR(kernel='linear', C=1e3)# 加入數(shù)據(jù),進(jìn)行擬合 svr_lin1.fit(dist1, temp_max1) svr_lin2.fit(dist2, temp_max2)# 關(guān)于 reshape 函數(shù)請(qǐng)看代碼后面的詳細(xì)討論 xp1 = np.arange(10,100,10).reshape((9,1)) xp2 = np.arange(50,400,50).reshape((7,1)) yp1 = svr_lin1.predict(xp1) yp2 = svr_lin2.predict(xp2)然后繪圖
# 限制了 x 軸的取值范圍 fig, ax = plt.subplots() ax.set_xlim(0,400)# 畫(huà)出圖像 ax.plot(xp1, yp1, c='b', label='Strong sea effect') ax.plot(xp2, yp2, c='g', label='Light sea effect') ax.plot(dist,temp_max,'ro')這里?np.arange(10,100,10)?會(huì)返回 [10, 20, 30,..., 90],如果把列表看成是一個(gè)矩陣,那么這個(gè)矩陣是 1?9 的。這里?reshape((9,1))?函數(shù)就會(huì)把該列表變?yōu)?9?1 的, [[10], [20], ..., [90]]。這么做的原因是因?yàn)?predict()?函數(shù)的只能接受一個(gè) N?1 的列表,返回一個(gè) 1?N 的列表。
上述代碼將生成如下圖 所示的圖像。
?
?如上所見(jiàn),離海 60 公里以?xún)?nèi),氣溫上升速度很快,從 28 度陡升至 31 度,隨后增速漸趨緩和(如果還繼續(xù)增長(zhǎng)的話(huà)),更長(zhǎng)的距離才會(huì)有小幅上升。這兩種趨勢(shì)可分別用兩條直線(xiàn)來(lái)表示,直線(xiàn)的表達(dá)式為:y=ax+by=ax+b 其中 a 為斜率,b 為截距。
你可能會(huì)考慮將這兩條直線(xiàn)的交點(diǎn)作為受海洋影響和不受海洋影響的區(qū)域的分界點(diǎn),或者至少是海洋影響較弱的分界點(diǎn)。
from scipy.optimize import fsolve# 定義了第一條擬合直線(xiàn) def line1(x):a1 = svr_lin1.coef_[0][0]b1 = svr_lin1.intercept_[0]return a1*x + b1# 定義了第二條擬合直線(xiàn) def line2(x):a2 = svr_lin2.coef_[0][0]b2 = svr_lin2.intercept_[0]return a2*x + b2# 定義了找到兩條直線(xiàn)的交點(diǎn)的 x 坐標(biāo)的函數(shù) def findIntersection(fun1,fun2,x0):return fsolve(lambda x : fun1(x) - fun2(x),x0)result = findIntersection(line1,line2,0.0) print("[x,y] = [ %d , %d ]" % (result,line1(result)))# x = [0,10,20, ..., 300] x = np.linspace(0,300,31) plt.plot(x,line1(x),x,line2(x),result,line1(result),'ro')?執(zhí)行上述代碼,將得到交點(diǎn)的坐標(biāo) [x,y]=[53,30][x,y]=[53,30] 并得到如圖所示的圖表。
?因此,你可以說(shuō)海洋對(duì)氣溫產(chǎn)生影響的平均距離(該天的情況)為 53 公里。現(xiàn)在,我們可以轉(zhuǎn)而分析最低氣溫。
# axis 函數(shù)規(guī)定了 x 軸和 y 軸的取值范圍 plt.axis((0,400,15,25)) plt.plot(dist,temp_min,'bo')在這個(gè)例子中,很明顯夜間或早上 6 點(diǎn)左右的最低溫與海洋無(wú)關(guān)。如果沒(méi)記錯(cuò)的話(huà),小時(shí)候老師教給大家的是海洋能夠緩和低溫,或者說(shuō)夜間海洋釋放白天吸收的熱量。但是從我們得到情況來(lái)看并非如此。我們剛使用的是意大利夏天的氣溫?cái)?shù)據(jù),而驗(yàn)證該假設(shè)在冬天或其他地方是否也成立,將會(huì)非常有趣。
濕度數(shù)據(jù)分析
10 個(gè) DataFrame 對(duì)象中還包含濕度這個(gè)氣象數(shù)據(jù)。因此,你也可以考察當(dāng)天三個(gè)近海城市和三個(gè)內(nèi)陸城市的濕度趨勢(shì)。
# 讀取濕度數(shù)據(jù) y1 = df_ravenna['humidity'] x1 = df_ravenna['day'] y2 = df_faenza['humidity'] x2 = df_faenza['day'] y3 = df_cesena['humidity'] x3 = df_cesena['day'] y4 = df_milano['humidity'] x4 = df_milano['day'] y5 = df_asti['humidity'] x5 = df_asti['day'] y6 = df_torino['humidity'] x6 = df_torino['day']# 重新定義 fig 和 ax 變量 fig, ax = plt.subplots() plt.xticks(rotation=70)# 把時(shí)間從 string 類(lèi)型轉(zhuǎn)化為標(biāo)準(zhǔn)的 datetime 類(lèi)型 day_ravenna = [parser.parse(x) for x in x1] day_faenza = [parser.parse(x) for x in x2] day_cesena = [parser.parse(x) for x in x3] day_milano = [parser.parse(x) for x in x4] day_asti = [parser.parse(x) for x in x5] day_torino = [parser.parse(x) for x in x6]# 規(guī)定時(shí)間的表示方式 hours = mdates.DateFormatter('%H:%M') ax.xaxis.set_major_formatter(hours)#表示在圖上 ax.plot(day_ravenna,y1,'r',day_faenza,y2,'r',day_cesena,y3,'r') ax.plot(day_milano,y4,'g',day_asti,y5,'g',day_torino,y6,'g')?乍看上去好像近海城市的濕度要大于內(nèi)陸城市,全天濕度差距在 20% 左右。我們?cè)賮?lái)看一下濕度的極值和離海遠(yuǎn)近之間的關(guān)系,是否跟我們的第一印象相符。
# 獲取最大濕度數(shù)據(jù) hum_max = [df_ravenna['humidity'].max(), df_cesena['humidity'].max(), df_faenza['humidity'].max(), df_ferrara['humidity'].max(), df_bologna['humidity'].max(), df_mantova['humidity'].max(), df_piacenza['humidity'].max(), df_milano['humidity'].max(), df_asti['humidity'].max(), df_torino['humidity'].max() ]plt.plot(dist,hum_max,'bo')我們把 10 個(gè)城市的最大濕度與離海遠(yuǎn)近之間的關(guān)系做成圖表?
?
# 獲取最小濕度 hum_min = [ df_ravenna['humidity'].min(), df_cesena['humidity'].min(), df_faenza['humidity'].min(), df_ferrara['humidity'].min(), df_bologna['humidity'].min(), df_mantova['humidity'].min(), df_piacenza['humidity'].min(), df_milano['humidity'].min(), df_asti['humidity'].min(), df_torino['humidity'].min() ] plt.plot(dist,hum_min,'bo')再來(lái)把 10 個(gè)城市的最小濕度與離海遠(yuǎn)近之間的關(guān)系做成圖表?
?由上兩幅圖可以確定,近海城市無(wú)論是最大還是最小濕度都要高于內(nèi)陸城市。然而,在我看來(lái),我們還不能說(shuō)濕度和距離之間存在線(xiàn)性關(guān)系或者其他能用曲線(xiàn)表示的關(guān)系。我們采集的數(shù)據(jù)點(diǎn)數(shù)量(10)太少,不足以描述這類(lèi)趨勢(shì)。
風(fēng)向頻率玫瑰圖
在我們采集的每個(gè)城市的氣象數(shù)據(jù)中,下面兩個(gè)與風(fēng)有關(guān):
- 風(fēng)力(風(fēng)向)
- 風(fēng)速
分析存放每個(gè)城市氣象數(shù)據(jù)的 DataFrame 就會(huì)發(fā)現(xiàn),風(fēng)速不僅跟一天的時(shí)間段相關(guān)聯(lián),還與一個(gè)介于 0~360 度的方向有關(guān)。
要表示呈 360 度分布的數(shù)據(jù)點(diǎn),最好使用另一種可視化方法:極區(qū)圖。
首先,創(chuàng)建一個(gè)直方圖,也就是將 360 度分為八個(gè)面元,每個(gè)面元為 45 度,把所有的數(shù)據(jù)點(diǎn)分到這八個(gè)面元中。
df_ravenna[['wind_deg','wind_speed','day']] hist, bins = np.histogram(df_ravenna['wind_deg'],8,[0,360])histogram() 函數(shù)返回結(jié)果中的數(shù)組 hist 為落在每個(gè)面元的數(shù)據(jù)點(diǎn)數(shù)量。[0 5 11 1 0 1 0 0]
返回結(jié)果中的數(shù)組 bins 定義了 360 度范圍內(nèi)各面元的邊界。[0. 45. 90. 135. 180. 225. 270. 315. 360.]
要想正確定義極區(qū)圖,離不開(kāi)這兩個(gè)數(shù)組。我們將創(chuàng)建一個(gè)函數(shù)來(lái)繪制極區(qū)圖,其中部分代碼在第 7 章已講過(guò)。我們把這個(gè)函數(shù)定義為 showRoseWind(),它有三個(gè)參數(shù):values 數(shù)組,指的是想為其作圖的數(shù)據(jù),也就是這里的 hist 數(shù)組;第二個(gè)參數(shù) city_name 為字符串類(lèi)型,指定圖表標(biāo)題所用的城市名稱(chēng);最后一個(gè)參數(shù) max_value 為整型,指定最大的藍(lán)色值。
定義這樣一個(gè)函數(shù)很有用,它既能避免多次重復(fù)編寫(xiě)相同的代碼,還能增強(qiáng)代碼的模塊化程度,便于你把精力放到與函數(shù)內(nèi)部操作相關(guān)的概念上。
def showRoseWind(values,city_name,max_value):N = 8# theta = [pi*1/4, pi*2/4, pi*3/4, ..., pi*2]theta = np.arange(2 * np.pi / 16, 2 * np.pi, 2 * np.pi / 8)radii = np.array(values)# 繪制極區(qū)圖的坐標(biāo)系plt.axes([0.025, 0.025, 0.95, 0.95], polar=True)# 列表中包含的是每一個(gè)扇區(qū)的 rgb 值,x越大,對(duì)應(yīng)的color越接近藍(lán)色colors = [(1-x/max_value, 1-x/max_value, 0.75) for x in radii]# 畫(huà)出每個(gè)扇區(qū)plt.bar(theta, radii, width=(2*np.pi/N), bottom=0.0, color=colors)# 設(shè)置極區(qū)圖的標(biāo)題plt.title(city_name, x=0.2, fontsize=20)?你需要修改變量 colors 存儲(chǔ)的顏色表。這里,扇形的顏色越接近藍(lán)色,值越大。定義好函數(shù)之后,調(diào)用它即可:
showRoseWind(hist,'Ravenna',max(hist))由圖可見(jiàn),整個(gè) 360 度的范圍被分成八個(gè)區(qū)域(面元),每個(gè)區(qū)域弧長(zhǎng)為 45 度,此外每個(gè)區(qū)域還有一列呈放射狀排列的刻度值。在每個(gè)區(qū)域中,用半徑長(zhǎng)度可以改變的扇形表示一個(gè)數(shù)值,半徑越長(zhǎng),扇形所表示的數(shù)值就越大。為了增強(qiáng)圖表的可讀性,我們使用與扇形半徑相對(duì)應(yīng)的顏色表。半徑越長(zhǎng),扇形跨度越大,顏色越接近于深藍(lán)色。
從剛得到的極區(qū)圖可以得知風(fēng)向在極坐標(biāo)系中的分布方式。該圖表示這一天大部分時(shí)間風(fēng)都吹向西南和正西方向。定義好 showRoseWind() 函數(shù)之后,查看其他城市的風(fēng)向情況也非常簡(jiǎn)單。
hist, bin = np.histogram(df_ferrara['wind_deg'],8,[0,360]) print(hist) showRoseWind(hist,'Ferrara', max(hist))?
實(shí)驗(yàn)總結(jié)
本實(shí)驗(yàn)主要目的是演示如何從原始數(shù)據(jù)獲取信息。其中有些信息無(wú)法給出重要結(jié)論,而有些信息能夠驗(yàn)證假設(shè),增加我們對(duì)系統(tǒng)狀態(tài)的認(rèn)識(shí),而找出這種信息也就意味著數(shù)據(jù)分析取得了成功。
總結(jié)
以上是生活随笔為你收集整理的数据分析案例-气象数据分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: NVIDIA Jetson TX2模块参
- 下一篇: 计算机主机发出滴滴声音怎么办,电脑蓝屏后