使用機器學習預測天氣(第一部分)

概述

  本章是使用機器學習預測天氣系列教程的第一部分,使用Python和機器學習來構建模型,根據從Weather Underground收集的數據來預測天氣溫度。該教程將由三個不一樣的部分組成,涵蓋的主題是:html

  • 數據收集和處理(本文)
  • 線性迴歸模型(第2章)
  • 神經網絡模型(第3章)

  本教程中使用的數據將從Weather Underground的免費層API服務中收集。我將使用python的requests庫來調用API,獲得從2015年起Lincoln, Nebraska的天氣數據。 一旦收集完成,數據將須要進行處理並彙總轉成合適的格式,而後進行清理。
  第二篇文章將重點分析數據中的趨勢,目標是選擇合適的特性並使用python的statsmodels和scikit-learn庫來構建線性迴歸模型。 我將討論構建線性迴歸模型,必須進行必要的假設,並演示如何評估數據特徵以構建一個健壯的模型。 並在最後完成模型的測試與驗證。
  最後的文章將着重於使用神經網絡。 我將比較構建神經網絡模型和構建線性迴歸模型的過程,結果,準確性。python

Weather Underground介紹

  Weather Underground是一家收集和分發全球各類天氣測量數據的公司。 該公司提供了大量的API,可用於商業和非商業用途。 在本文中,我將介紹如何使用非商業API獲取每日天氣數據。因此,若是你跟隨者本教程操做的話,您須要註冊他們的免費開發者賬戶。 此賬戶提供了一個API密鑰,這個密鑰限制,每分鐘10個,天天500個API請求。
  獲取歷史數據的API以下:json

http://api.wunderground.com/api/API_KEY/history_YYYYMMDD/q/STATE/CITY.json
  • API_KEY: 註冊帳戶獲取
  • YYYYMMDD: 你想要獲取的天氣數據的日期
  • STATE: 州名縮寫
  • CITY: 你請求的城市名

調用API

  本教程調用Weather Underground API獲取歷史數據時,用到以下的python庫。api

名稱 描述 來源
datetime 處理日期 標準庫
time 處理時間 標準庫
collections 使用該庫的namedtuples來結構化數據 標準庫
pandas 處理數據 第三方
requests HTTP請求處理庫 第三方
matplotlib 製圖庫 第三方

  好,咱們先導入這些庫:網絡

from datetime import datetime, timedelta  
import time  
from collections import namedtuple  
import pandas as pd  
import requests  
import matplotlib.pyplot as plt

接下里,定義常量來保存API_KEY和BASE_URL,注意,例子中的API_KEY不可用,你要本身註冊獲取。代碼以下:數據結構

API_KEY = '7052ad35e3c73564'  
# 第一個大括號是API_KEY,第二個是日期
BASE_URL = "http://api.wunderground.com/api/{}/history_{}/q/NE/Lincoln.json"

而後咱們初始化一個變量,存儲日期,而後定義一個list,指明要從API返回的內容裏獲取的數據。而後定義一個namedtuple類型的變量DailySummary來存儲返回的數據。代碼以下:app

target_date = datetime(2016, 5, 16)  
features = ["date", "meantempm", "meandewptm", "meanpressurem", "maxhumidity", "minhumidity", "maxtempm",  
            "mintempm", "maxdewptm", "mindewptm", "maxpressurem", "minpressurem", "precipm"]
DailySummary = namedtuple("DailySummary", features)

定義一個函數,調用API,獲取指定target_date開始的days天的數據,代碼以下:機器學習

def extract_weather_data(url, api_key, target_date, days):  
    records = []
    for _ in range(days):
        request = BASE_URL.format(API_KEY, target_date.strftime('%Y%m%d'))
        response = requests.get(request)
        if response.status_code == 200:
            data = response.json()['history']['dailysummary'][0]
            records.append(DailySummary(
                date=target_date,
                meantempm=data['meantempm'],
                meandewptm=data['meandewptm'],
                meanpressurem=data['meanpressurem'],
                maxhumidity=data['maxhumidity'],
                minhumidity=data['minhumidity'],
                maxtempm=data['maxtempm'],
                mintempm=data['mintempm'],
                maxdewptm=data['maxdewptm'],
                mindewptm=data['mindewptm'],
                maxpressurem=data['maxpressurem'],
                minpressurem=data['minpressurem'],
                precipm=data['precipm']))
        time.sleep(6)
        target_date += timedelta(days=1)
    return records

首先,定義個list records,用來存放上述的DailySummary,使用for循環來遍歷指定的全部日期。而後生成url,發起HTTP請求,獲取返回的數據,使用返回的數據,初始化DailySummary,最後存放到records裏。經過這個函數的出,就能夠獲取到指定日期開始的N天的歷史天氣數據,並返回。函數

獲取500天的天氣數據

  因爲API接口的限制,咱們須要兩天的時間才能獲取到500天的數據。你也能夠下載個人測試數據,來節約你的時間。學習

records = extract_weather_data(BASE_URL, API_KEY, target_date, 500)

格式化數據爲Pandas DataFrame格式

  咱們使用DailySummary列表來初始化Pandas DataFrame。DataFrame數據類型是機器學習領域常常會用到的數據結構。

df = pd.DataFrame(records, columns=features).set_index('date')

特徵提取

  機器學習是帶有實驗性質的,因此,你可能遇到一些矛盾的數據或者行爲。所以,你須要在你用機器學習處理問題是,你須要對處理的問題領域有必定的瞭解,這樣能夠更好的提取數據特徵。
  我將採用以下的數據字段,而且,使用過去三天的數據做爲預測。

  • mean temperature
  • mean dewpoint
  • mean pressure
  • max humidity
  • min humidity
  • max dewpoint
  • min dewpoint
  • max pressure
  • min pressure
  • precipitation

首先我須要在DataFrame裏增長一些字段來保存新的數據字段,爲了方便測試,我建立了一個tmp變量,存儲10個數據,這些數據都有meantempm和meandewptm屬性。代碼以下:

tmp = df[['meantempm', 'meandewptm']].head(10)  
tmp

對於每一行的數據,咱們分別獲取他前一天、前兩天、前三天對應的數據,存在本行,分別以屬性_index來命名,代碼以下:

# 1 day prior
N = 1

# target measurement of mean temperature
feature = 'meantempm'

# total number of rows
rows = tmp.shape[0]

# a list representing Nth prior measurements of feature
# notice that the front of the list needs to be padded with N
# None values to maintain the constistent rows length for each N
nth_prior_measurements = [None]*N + [tmp[feature][i-N] for i in range(N, rows)]

# make a new column name of feature_N and add to DataFrame
col_name = "{}_{}".format(feature, N)  
tmp[col_name] = nth_prior_measurements  
tmp

咱們如今把上面的處理過程封裝成一個函數,方便調用。

def derive_nth_day_feature(df, feature, N):  
    rows = df.shape[0]
    nth_prior_measurements = [None]*N + [df[feature][i-N] for i in range(N, rows)]
    col_name = "{}_{}".format(feature, N)
    df[col_name] = nth_prior_measurements

好,咱們如今對全部的特徵,都取過去三天的數據,放在本行。

for feature in features:  
    if feature != 'date':
        for N in range(1, 4):
            derive_nth_day_feature(df, feature, N)

處理完後,咱們如今的全部數據特徵爲:

df.columns  

Index(['meantempm', 'meandewptm', 'meanpressurem', 'maxhumidity',  
       'minhumidity', 'maxtempm', 'mintempm', 'maxdewptm', 'mindewptm',
       'maxpressurem', 'minpressurem', 'precipm', 'meantempm_1', 'meantempm_2',
       'meantempm_3', 'meandewptm_1', 'meandewptm_2', 'meandewptm_3',
       'meanpressurem_1', 'meanpressurem_2', 'meanpressurem_3',
       'maxhumidity_1', 'maxhumidity_2', 'maxhumidity_3', 'minhumidity_1',
       'minhumidity_2', 'minhumidity_3', 'maxtempm_1', 'maxtempm_2',
       'maxtempm_3', 'mintempm_1', 'mintempm_2', 'mintempm_3', 'maxdewptm_1',
       'maxdewptm_2', 'maxdewptm_3', 'mindewptm_1', 'mindewptm_2',
       'mindewptm_3', 'maxpressurem_1', 'maxpressurem_2', 'maxpressurem_3',
       'minpressurem_1', 'minpressurem_2', 'minpressurem_3', 'precipm_1',
       'precipm_2', 'precipm_3'],
      dtype='object')

數據清洗

  數據清洗時機器學習過程當中最重要的一步,並且很是的耗時、費力。本教程中,咱們會去掉不須要的樣本、數據不完整的樣本,查看數據的一致性等。
  首先去掉我不感興趣的數據,來減小樣本集。咱們的目標是根據過去三天的天氣數據預測天氣溫度,所以咱們只保留min, max, mean三個字段的數據。

# make list of original features without meantempm, mintempm, and maxtempm
to_remove = [feature  
             for feature in features 
             if feature not in ['meantempm', 'mintempm', 'maxtempm']]

# make a list of columns to keep
to_keep = [col for col in df.columns if col not in to_remove]

# select only the columns in to_keep and assign to df
df = df[to_keep]  
df.columns
Index(['meantempm', 'maxtempm', 'mintempm', 'meantempm_1', 'meantempm_2',  
       'meantempm_3', 'meandewptm_1', 'meandewptm_2', 'meandewptm_3',
       'meanpressurem_1', 'meanpressurem_2', 'meanpressurem_3',
       'maxhumidity_1', 'maxhumidity_2', 'maxhumidity_3', 'minhumidity_1',
       'minhumidity_2', 'minhumidity_3', 'maxtempm_1', 'maxtempm_2',
       'maxtempm_3', 'mintempm_1', 'mintempm_2', 'mintempm_3', 'maxdewptm_1',
       'maxdewptm_2', 'maxdewptm_3', 'mindewptm_1', 'mindewptm_2',
       'mindewptm_3', 'maxpressurem_1', 'maxpressurem_2', 'maxpressurem_3',
       'minpressurem_1', 'minpressurem_2', 'minpressurem_3', 'precipm_1',
       'precipm_2', 'precipm_3'],
      dtype='object')

爲了更好的觀察數據,咱們使用Pandas的一些內置函數來查看數據信息,首先咱們使用info()函數,這個函數會輸出DataFrame裏存放的數據信息。

df.info()
<class 'pandas.core.frame.DataFrame'>  
DatetimeIndex: 1000 entries, 2015-01-01 to 2017-09-27  
Data columns (total 39 columns):  
meantempm          1000 non-null object  
maxtempm           1000 non-null object  
mintempm           1000 non-null object  
meantempm_1        999 non-null object  
meantempm_2        998 non-null object  
meantempm_3        997 non-null object  
meandewptm_1       999 non-null object  
meandewptm_2       998 non-null object  
meandewptm_3       997 non-null object  
meanpressurem_1    999 non-null object  
meanpressurem_2    998 non-null object  
meanpressurem_3    997 non-null object  
maxhumidity_1      999 non-null object  
maxhumidity_2      998 non-null object  
maxhumidity_3      997 non-null object  
minhumidity_1      999 non-null object  
minhumidity_2      998 non-null object  
minhumidity_3      997 non-null object  
maxtempm_1         999 non-null object  
maxtempm_2         998 non-null object  
maxtempm_3         997 non-null object  
mintempm_1         999 non-null object  
mintempm_2         998 non-null object  
mintempm_3         997 non-null object  
maxdewptm_1        999 non-null object  
maxdewptm_2        998 non-null object  
maxdewptm_3        997 non-null object  
mindewptm_1        999 non-null object  
mindewptm_2        998 non-null object  
mindewptm_3        997 non-null object  
maxpressurem_1     999 non-null object  
maxpressurem_2     998 non-null object  
maxpressurem_3     997 non-null object  
minpressurem_1     999 non-null object  
minpressurem_2     998 non-null object  
minpressurem_3     997 non-null object  
precipm_1          999 non-null object  
precipm_2          998 non-null object  
precipm_3          997 non-null object  
dtypes: object(39)  
memory usage: 312.5+ KB

注意:每一行的數據類型都是object,咱們須要把數據轉成float。

df = df.apply(pd.to_numeric, errors='coerce')  
df.info()
<class 'pandas.core.frame.DataFrame'>  
DatetimeIndex: 1000 entries, 2015-01-01 to 2017-09-27  
Data columns (total 39 columns):  
meantempm          1000 non-null int64  
maxtempm           1000 non-null int64  
mintempm           1000 non-null int64  
meantempm_1        999 non-null float64  
meantempm_2        998 non-null float64  
meantempm_3        997 non-null float64  
meandewptm_1       999 non-null float64  
meandewptm_2       998 non-null float64  
meandewptm_3       997 non-null float64  
meanpressurem_1    999 non-null float64  
meanpressurem_2    998 non-null float64  
meanpressurem_3    997 non-null float64  
maxhumidity_1      999 non-null float64  
maxhumidity_2      998 non-null float64  
maxhumidity_3      997 non-null float64  
minhumidity_1      999 non-null float64  
minhumidity_2      998 non-null float64  
minhumidity_3      997 non-null float64  
maxtempm_1         999 non-null float64  
maxtempm_2         998 non-null float64  
maxtempm_3         997 non-null float64  
mintempm_1         999 non-null float64  
mintempm_2         998 non-null float64  
mintempm_3         997 non-null float64  
maxdewptm_1        999 non-null float64  
maxdewptm_2        998 non-null float64  
maxdewptm_3        997 non-null float64  
mindewptm_1        999 non-null float64  
mindewptm_2        998 non-null float64  
mindewptm_3        997 non-null float64  
maxpressurem_1     999 non-null float64  
maxpressurem_2     998 non-null float64  
maxpressurem_3     997 non-null float64  
minpressurem_1     999 non-null float64  
minpressurem_2     998 non-null float64  
minpressurem_3     997 non-null float64  
precipm_1          889 non-null float64  
precipm_2          889 non-null float64  
precipm_3          888 non-null float64  
dtypes: float64(36), int64(3)  
memory usage: 312.5 KB

如今獲得我想要的數據了。接下來咱們調用describe()函數,這個函數會返回一個DataFrame,這個返回值包含了總數、平均數、標準差、最小、25%、50%、75%、最大的數據信息。

  接下來,使用四分位的方法,去掉25%數據裏特別小的和75%數據裏特別大的數據。

# Call describe on df and transpose it due to the large number of columns
spread = df.describe().T

# precalculate interquartile range for ease of use in next calculation
IQR = spread['75%'] - spread['25%']

# create an outliers column which is either 3 IQRs below the first quartile or
# 3 IQRs above the third quartile
spread['outliers'] = (spread['min']<(spread['25%']-(3*IQR)))|(spread['max'] > (spread['75%']+3*IQR))

# just display the features containing extreme outliers
spread.ix[spread.outliers,]


  評估異常值的潛在影響是任何分析項目的難點。 一方面,您須要關注引入虛假數據樣本的可能性,這些樣本將嚴重影響您的模型。 另外一方面,異常值對於預測在特殊狀況下出現的結果是很是有意義的。 咱們將討論每個包含特徵的異常值,看看咱們是否可以得出合理的結論來處理它們。

  第一組特徵看起來與最大溼度有關。 觀察這些數據,我能夠看出,這個特徵類別的異常值是很是低的最小值。這數據看起來沒價值,我想我想仔細看看它,最好是以圖形方式。 要作到這一點,我會使用直方圖。

%matplotlib inline
plt.rcParams['figure.figsize'] = [14, 8]  
df.maxhumidity_1.hist()  
plt.title('Distribution of maxhumidity_1')  
plt.xlabel('maxhumidity_1')  
plt.show()


查看maxhumidity字段的直方圖,數據表現出至關多的負偏移。 在選擇預測模型和評估最大溼度影響的強度時,我會牢記這一點。 許多基本的統計方法都假定數據是正態分佈的。 如今咱們暫時無論它,可是記住這個異常特性。

  接下來咱們看另一個字段的直方圖

df.minpressurem_1.hist()  
plt.title('Distribution of minpressurem_1')  
plt.xlabel('minpressurem_1')  
plt.show()

  要解決的最後一個數據質量問題是缺失值。 因爲我構建DataFrame的時候,缺乏的值由NaN表示。 您可能會記得,我經過推導表明前三天測量結果的特徵,有意引入了收集數據前三天的缺失值。 直到第三天咱們才能開始推導出這些特徵,因此很明顯我會想把這些頭三天從數據集中排除出去。
再回頭再看一下上面info()函數輸出的信息,能夠看到包含NaN值的數據特徵很是的少,除了我提到的幾個字段,基本就沒有了。由於機器學習須要樣本字段數據的完整性,由於若是咱們由於降水量那個字段爲空,就去掉樣本,那麼會形成大量的樣本不可用,對於這種狀況,咱們能夠給爲空的降水量字段的樣本填入一個值。根據經驗和儘可能減小因爲填入的值對模型的影響,我決定給爲空的降水量字段填入值0。

# iterate over the precip columns
for precip_col in ['precipm_1', 'precipm_2', 'precipm_3']:  
    # create a boolean array of values representing nans
    missing_vals = pd.isnull(df[precip_col])
    df[precip_col][missing_vals] = 0

填入值後,咱們就能夠刪掉字段值爲空的樣本了,只用調用dropna()函數。

df = df.dropna()

總結

  這篇文章主要介紹了數據的收集、處理、清洗的流程,本篇文章處理完的處理,將用於下篇文章的模型訓練。
  對你來講,這篇文章可能很枯燥,沒啥乾貨,但好的樣本數據,才能訓練處好的模型,所以,樣本數據的收集和處理能力,直接影響你後面的機器學習的效果。

英文原文

轉自個人博客,捕蛇者說

相關文章
相關標籤/搜索