记录自己使用循环神经网络对天气进行预测的过程
时间:2022-12-15 12:30:01
本实验中使用的数据来自链接: http://www.tianqihoubao.com/lishi/,选定的城市是广西客人,将使用2011-2021年的数据。机器学习的框架是tensorflow2.3.0。
1.数据爬取
首先,我们必须在这里抓取数据并使用它python爬虫中最常见的requests库和BeautifulSoup库,以下是数据爬取craw.py
import requests from bs4 import BeautifulSoup import numpy as np import pandas as pd import re def data_list(numpy): z = [] for i in numpy: if i.string is not None: z.append(i.string) for m, n in enumerate(z): z[m] = n.replace('\n', '').replace(' ', '') return z class Grab: def __init__(self, url): self.url = url self.header = {
'User-Agent': r'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, ' r'like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32'} def analysis(self): for i in range(3): try: resp = requests.get(url=self.url, headers=self.header, proxies=None, timeout=(3, 7))
if resp.status_code == 200:
break
except:
print('请求超时!')
resp.encoding = 'gbk'
html = BeautifulSoup(resp.text, 'html5lib')
return html
def location(self, str1):
text = self.analysis()
data = text.find_all(str1)
return data
def data(self):
data = self.location('td')
data = data_list(data)
data = np.array(data).reshape(-1, 3)
data_date = self.date()
data = np.c_[data_date, data]
print(data)
return data
def date(self):
date = self.location('a')
date = data_list(date)
data_str = ''.join(date)
date = re.findall(r'\d\d\d\d年\d\d月\d\d日', data_str)
return date
if __name__ == "__main__":
flag = 0
for i in range(2011, 2021 + 1):
for j in range(1, 13):
ur = r'http://www.tianqihoubao.com/lishi/laibin/month/{}{:0>2}.html'.format(i, j)
a = Grab(ur)
b = a.data()
if flag == 0:
weather = b.copy()
flag += 1
else:
weather = np.r_[weather, b]
if i == 2022:
if j == 5:
break
c = ['日期', '天气', '气温', '风力风向']
df = pd.DataFrame(data=weather, columns=c)
df.to_csv('data.csv', header=True, index=False, encoding='gbk')
下面说一下思路:
这个网站对于每一个月的数据都会做成一个网页,而且格式也是固定的形式,这样我们只需要两个for循环就能得到所有想要的网页网址
for i in range(2011, 2021 + 1):
for j in range(1, 13):
ur = r'http://www.tianqihoubao.com/lishi/laibin/month/{}{:0>2}.html'.format(i, j)
format是格式化输入,用于控制输入的年份和月份,由于有些月份前面会带一个0,所以这里使用了{:0>2}来让每个数字都左对齐两位。
下面进入到Grab类,初始化从循环中得到的网址和请求头。
def __init__(self, url):
self.url = url
self.header = {
'User-Agent': r'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, '
r'like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32'}
请求头可以去百度一下常用的即可。
接下来就是请求访问了,调用requests库中的get方法(这里由于这个网址有时会请求失败导致一直卡住,所以设置了一个for循环来多次进行请求访问),get中timeout=(3,7)表示连接时间为3,响应时间为7,使用得当可以提高访问速度,节省时间。resp.status_code根据自己实际访问的网页来填写,编码使用汉字最常见的gbk,最终再使用BeautifulSoup根据html5lib格式来解析即可得到一个类似其实就是 json格式的文本。
def analysis(self):
for i in range(3):
try:
resp = requests.get(url=self.url, headers=self.header,
proxies=None, timeout=(3, 7))
if resp.status_code == 200:
break
except:
print('请求超时!')
resp.encoding = 'gbk'
html = BeautifulSoup(resp.text, 'html5lib')
return html
将得到的文本复制到记事本里分析一下
想要的数据的标签都在td里,但是时间轴却在a里。那我们先对数据动手,为了方便下面多次访问不同的标签,写了一个可以多次调用的访问函数
def location(self, str1):
text = self.analysis()
data = text.find_all(str1)
return data
下面寻找td标签下的所有数据
def data(self):
data = self.location('td')
data = data_list(data)
data = np.array(data).reshape(-1, 3)
data_date = self.date()
data = np.c_[data_date, data]
print(data)
return data
这里还调用了一个data_list函数,是为了把所有的数据字符串提取出并且去掉空内容和换行符。这样我们会得到一个装了所有数据的列表,由于每个样本的数据都是3列,这里直接就可以创建一个数组再重新塑形就能得到想要的样子:
还得想办法把日期也放进去,因此创建了一个date函数来将日期抓取下来:
def date(self):
date = self.location('a')
date = data_list(date)
data_str = ''.join(date)
date = re.findall(r'\d\d\d\d年\d\d月\d\d日', data_str)
return date
同样的也是也先调用访问函数,然后再调用data_list函数得到一个装满时间数据的列表,和气象数据不同的是还有很多不需要的数据:
这里我的做法是使用join将列表中的字符串拼接成一个字符串,再使用re库中的findall函数正则匹配\d\d\d\d年\d\d月\d\d日
型的字符串,这样就能将这个月的时间都装进一个列表了(其实这里的时间有一个更明确的标签 ):如果使用for加格式化输入遍历应该也能得到,但是我嫌还要处理不同的2月天数就放弃了
然后再看回data函数,直接使用c_将气象数据和天气数据合在一起,最终就将一个月的数据得到了:
至此主函数中的a = Grab(ur) b = a.data()
也执行完毕,下面就是将每个月的数组纵向拼接在一起,最终创建一个datafarme将其保持为csv文件方便后续的使用。
2.数据预处理
一个机器学习的过程,可能有80%的时间在进行数据预处理,有19%的时间在埋怨数据的不足之处,还有1%在训练模型。对于这个天气数据我就不得不想吐槽数据也太少了,如果后面用5天来预测一天的天气,数据可能就几百行,更别说这么多种天气,还基本没有什么周期性,就用天气,温度和风向风级来预测好像确实会有一点点离谱,而且该数据的风向风级在2017年之前几乎都是北风小于等于3级。
用于数据预处理的data.py
由于数据量的大小,所以觉得并不需要用生成器。
import numpy as np
import pandas as pd
from collections import Counter
import matplotlib.pyplot as plt
import re
def Get_label_dict(y):
dict_l = {
}
for i, j in enumerate(y):
dict_l[j] = i
return dict_l
def F(matrix, num):
results = np.zeros((len(matrix), num))
for i, j in enumerate(matrix):
results[i, j] = 1
return results
def load_data(data_dir):
weather_data = Weather(data_dir).pre()
data = (weather_data.loc[:, ['白天天气', '夜间天气', '最高温度', '最低温度', '白天风力风向', '夜间风力风向', '白天风级', '夜间风级']]).to_numpy()
label = weather_data['白天天气'].to_numpy()
train = data[:1].reshape(-1, 1, data.shape[-1])
train_label = label[1]
for i in range(2, data.shape[0], 2):
index = i + 1
if index >= data.shape[0]:
break
else:
train = np.r_[train, data[i:index].reshape(-1, 1, data.shape[-1])]
train_label = np.r_[train_label, label[index]]
train_label = F(train_label, 13).astype('float32')
# mean = np.mean(train, axis=0)
# std = np.std(train, axis=0)
# train = (train - mean) / std
return train, train_label
class Weather:
def __init__(self, file_dir):
self.data = pd.read_csv(file_dir, encoding='gbk')
def pre(self):
col = ['天气', '气温', '风力风向']
list_col = ['白天天气', '夜间天气', '最高温度', '最低温度', '白天风力风向', '夜间风力风向', '白天风级', '夜间风级']
q = 0
for i, j in enumerate(col):
new_data = self.data[j].str.split('/', 1, True)
new_data.columns = [list_col[q], list_col[q + 1]]
q += 2
self.data = self.data.drop([j], axis=1).join(new_data)
self.data['日期'] = self.data['日期'].apply(lambda x: x.replace('年', '-').replace('月', '-').replace('日', ''))
self.data['日期'] = pd.to_datetime(self.data['日期'])
self.data['最高温度'] = self.data['最高温度'].apply(lambda x: int(x.replace('℃', '')))
self.data['最低温度'] = self.data['最低温度'].apply(lambda x: int(x.replace('℃', '')))
weather_c = Counter(self.data['白天天气'])
weather_dict = Get_label_dict(weather_c)
self.data['白天天气'] = self.data['白天天气'].apply(lambda x: weather_dict[x])
self.data['夜间天气'] = self.data['夜间天气'].apply(lambda x: weather_dict[x])
self.data['白天风力风向'] = self.data['白天风力风向'].str.replace('微风', '3级')
self.data['白天风级'] = self.data['白天风力风向'].apply(lambda x: x.replace(re.findall('[\u4e00-\u9fa5]+', x)[0], ''))
wind_level_c = Counter(self.data['白天风级'])
wind_level_dict = Get_label_dict(wind_level_c)
self.data['白天风级'] = self.data['白天风级'].apply(lambda x: wind_level_dict[x])
self.data['白天风力风向'] = self.data['白天风力风向'].apply(lambda x: re.findall('[\u4e00-\u9fa5]+', x)[0])
wind_direction_c = Counter(self.data['白天风力风向'])
wind_direction_dict = Get_label_dict(wind_direction_c)
self.data['白天风力风向'] = self.data['白天风力风向'].apply(lambda x: wind_direction_dict[x])
self.data['夜间风力风向'] = self.data['夜间风力风向'].str.replace('微风', '3级')
self.data['夜间风级'] = self.data['夜间风力风向'].apply(lambda x: x.replace(re.findall('[\u4e00-\u9fa5]+', x)[0], ''))
wind_level_c = Counter(self.data['夜间风级'])
wind_level_dict = Get_label_dict(wind_level_c)
self.data['夜间风级'] = self.data['夜间风级'].apply(lambda x: wind_level_dict[x])
self.data['夜间风力风向'] = self.data['夜间风力风向'].apply(lambda x: re.findall('[\u4e00-\u9fa5]+', x)[0])
wind_direction_c = Counter(self.data['夜间风力风向'])
wind_direction_dict = Get_label_dict(wind_direction_c)
self.data['夜间风力风向'] = self.data['夜间风力风向'].apply(lambda x: wind_direction_dict[x])
df = self.data
return df
# def str_vector(self):
# wind = self.pre()['白天风力风向'].to_list()
# wind_str = []
# wind_str_dict = {}
# data_str = []
# for i, j in enumerate(wind):
# medium = []
# medium.extend(j)
# wind_str.extend(j)
# data_str.append(medium)
# wind_str = set(wind_str)
# for m, n in enumerate(wind_str):
# wind_str_dict[n] = m
# result = np.zeros((4022, 40))
# for z, x in enumerate(data_str):
# for v in x:
# result[z, wind_str_dict[v]] = 1
# return result
我们先将白天夜间天气,风向风级以及最高最低温度分开来:
def pre(self):
col = ['天气', '气温', '风力风向']
list_col = ['白天天气', '夜间天气', '最高温度', '最低温度', '白天风力风向', '夜间风力风向', '白天风级', '夜间风级']
q = 0
for i, j in enumerate(col):
new_data = self.data[j].str.split('/', 1, True)
new_data.columns = [list_col[q], list_col[q + 1]]
q += 2
self.data = self.data.drop([j], axis=1).join(new_data)
print(self.data)
然后我们可以查看一下温度情况(下面的图是基于已经预处理完的数据):
如果这是一个温度回归预测,那么我们甚至只用温度的数据都能预测出未来的温度,可惜这是一个天气分类问题,天气就没有这么好的规律了:
下面继续对数据进行处理,日期随便处不处理,将温度后面的符号去掉:
self.data['日期'] = self.data['日期'].apply(lambda x: x.replace('年', '-').replace('月', '-').replace('日', ''))
self.data['日期'] = pd.to_datetime(self.data['日期'])
self.data['最高温度'] = self.data['最高温度'].apply(lambda x: int(x.replace('℃', '')))
self.data['最低温度'] = self.data['最低温度'].apply(lambda x: int(x.replace('℃', '')))
天气的话使用collections库下的Counter
来统计所有出现