视频作者:菜菜TsaiTsai
链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili
常识上来说,我们认为地点肯定是对明天是否会下雨存在影响的。比如说,如果其他信息都不给出,我们只猜测,“伦敦明天是否会下雨”和”北京明天是否会下雨“,我一定会猜测伦敦会下雨,而北京不会,因为伦敦是常年下雨的城市,而北京的气候非常干燥。对澳大利亚这样面积巨大的国家来说,必然存在着不同的城市有着不同的下雨倾向的情况。但尴尬的是,和时间一样,我们输入地点的名字对于算法来说,就是一串字符,"London"和"Beijing"对算法来说,和0,1没有区别。同样,我们的样本中含有49个不同地点,如果做成分类型变量,算法就无法辨别它究竟是否是分类变量。也就是说,我们需要让算法意识到,不同的地点因为气候不同,所以对“明天是否会下雨”有着不同的影响。如果我们能够将地点转换为这个地方的气候的话,我们就可以将不同城市打包到同一个气候中,而同一个气候下反应的降雨情况应该是相似的
由于直接搜索气象站可能无法获得气候信息,所以我们通过爬虫获得澳大利亚主要城市的气候信息、这些城市的经纬度、气象站的经纬度,通过气象站离哪个主要城市最近,来用该城市的气候,决定气象站的气候
在地理上,两个地点之间的距离,由如下公式来进行计算:
d i s t = R × arccos ( sin ( s l a t ) × sin ( e l a t ) + cos ( s l a t ) × cos ( e l a t ) × cos ( s l o n − e l o n ) ) dist=R \times \arccos(\sin (slat)\times \sin (elat)+\cos (slat)\times \cos (elat)\times \cos (slon-elon)) dist=R×arccos(sin(slat)×sin(elat)+cos(slat)×cos(elat)×cos(slon−elon))
其中R是地球的半径,6371.01km,arccos是三角反余弦函数,slat是起始地点的纬度,slon是起始地点的经度, elat是结束地点的纬度,elon是结束地点的经度。本质还是计算两点之间的距离。而我们爬取的经纬度,本质其实是角度,所以需要用各种三角函数和弧度公式将角度转换成距离。
澳大利亚主要城市所对应的气候类型数据,并保存在csv文件city_climate.csv当中,每个城市所对应的经纬度,并保存在数据cityll.csv当中
cityll = pd.read_csv(r"D:\ObsidianWorkSpace\SklearnData\cityll.csv",index_col=0)
city_climate = pd.read_csv(r"D:\ObsidianWorkSpace\SklearnData\Cityclimate.csv")
cityll.head() # 每个城市对应的经纬度
---
City Latitude Longitude Latitudedir Longitudedir
0 Adelaide 34.9285° 138.6007° S, E
1 Albany 35.0275° 117.8840° S, E
2 Albury 36.0737° 146.9135° S, E
city_climate.head() # 每个城市对应的气候
---
City Climate
0 Adelaide Warm temperate
1 Albany Mild temperate
2 Albury Hot dry summer, cool winter
接下来,我们来将这两张表处理成可以使用的样子,首先要去掉cityll中经纬度上带有的度数符号,然后要将两张表合并起来。(这两张表的City是能对应起来的,这是因为爬虫爬取数据的时候设置的)
# 依旧是先处理一个元素然后推广
cityll.loc[0,"Latitude"][:-1] # 对字符串进行切片
---
'34.9285'
float(cityll.loc[0,"Latitude"][:-1])
---
34.9285
cityll['Latitudenum'] = cityll['Latitude'].apply(lambda x:float(x[:-1]))
cityll['Longitudenum'] = cityll['Longitude'].apply(lambda x:float(x[:-1]))
# 这里度数可能是南纬北纬,东经西经,所以我们需要确定是否相同
cityll.loc[:,'Latitudedir'].value_counts()
---
S, 100
Name: Latitudedir, dtype: int64
cityll.loc[:,'Longitudedir'].value_counts()
---
E 100
Name: Longitudedir, dtype: int64
# 发现相同,因此不需要担心,所以经纬度的方向我们可以舍弃了
citylld = cityll.iloc[:,[0,5,6]]
citylld.head()
---
City Latitudenum Longitudenum
0 Adelaide 34.9285 138.6007
1 Albany 35.0275 117.8840
2 Albury 36.0737 146.9135
# 将city_climate中的气候添加到citylld中
citylld['climate'] = city_climate.iloc[:,-1]
# 这两个表在制作的时候City就是能对应起来的
# 如果不能对应起来,需要使用map
citylld.head() # 澳大利亚常见城市的经纬度
---
City Latitudenum Longitudenum climate
0 Adelaide 34.9285 138.6007 Warm temperate
1 Albany 35.0275 117.8840 Mild temperate
2 Albury 36.0737 146.9135 Hot dry summer, cool winter
citylld.loc[:,'climate'].value_counts()
---
Hot dry summer, cool winter 24
Hot dry summer, warm winter 18
Warm temperate 18
High humidity summer, warm winter 17
Cool temperate 9
Mild temperate 9
Warm humid summer, mild winter 5
Name: climate, dtype: int64
爬取训练集中所有的地点所对应的经纬度,并且保存在一个csv文件samplecity.csv中
samplecity = pd.read_csv(r'D:\ObsidianWorkSpace\SklearnData\samplecity.csv',index_col=0)
samplecity.head()
---
City Latitude Longitude Latitudedir Longitudedir
0 Canberra 35.2809° 149.1300° S, E
1 Sydney 33.8688° 151.2093° S, E
2 Perth 31.9505° 115.8605° S, E
# 取度数,转float
samplecity['Latitudenum'] = samplecity['Latitude'].apply(lambda x:float(x[:-1]))
samplecity['Longitudenum'] = samplecity['Longitude'].apply(lambda x:float(x[:-1]))
samplecityd = samplecity.iloc[:,[0,5,6]]
samplecityd.head() # 样本城市的经纬度
---
City Latitudenum Longitudenum
0 Canberra 35.2809 149.1300
1 Sydney 33.8688 151.2093
2 Perth 31.9505 115.8605
# 已知经纬度计算两点的距离
from math import radians, sin, cos, acos
# 我们已知的是角度,但是三角函数计算的时候需要弧度
citylld.loc[:,'slat'] = citylld.iloc[:,1].apply(lambda x:radians(x))
citylld.loc[:,'slon'] = citylld.iloc[:,2].apply(lambda x:radians(x))
samplecityd.loc[:,'elat'] = samplecityd.iloc[:,1].apply(lambda x:radians(x))
samplecityd.loc[:,'elon'] = samplecityd.iloc[:,2].apply(lambda x:radians(x))
import sys # 这个我不知道是干啥的
for i in range(samplecityd.shape[0]):
slat = citylld.loc[:,'slat']
slon = citylld.loc[:,'slon']
elat = samplecityd.loc[i,'elat']
elon = samplecityd.loc[i,'elon']
dist = 6371.01 * np.arccos(np.sin(slat) * np.sin(elat) +
np.cos(slat) * np.cos(elat) * np.cos(slon.values - elon)) # 计算到每个已知城市的距离
city_index = np.argsort(dist)[0]
# 每次计算后,取距离最近的城市,然后将最近的城市和城市对应的气候都匹配到samplecityd中
samplecityd.loc[i,"closest_city"] = citylld.loc[city_index,"City"]
# 用于验证是否编写有问题,在地图上查,如果City和closest_city距离很远可能上面编写有问题
samplecityd.loc[i,'climate'] = citylld.loc[city_index,"climate"]
这里说一下代码中的这一行
dist = 6371.01 * np.arccos(np.sin(slat) * np.sin(elat) + np.cos(slat) * np.cos(elat) * np.cos(slon.values - elon))
np.array([1,2])*np.array([3,4]) # 一维数组相乘等于对应元素相乘 --- array([3, 8])
说一下np.argsort
np.argsort(np.array([1,2,4,3])) # 返回索引列表,返回列表的第一个元素就是之前输入列表的最小值的索引 --- array([0, 1, 3, 2], dtype=int64)
samplecityd.head()
---
City Latitudenum Longitudenum elat elon closest_city climate
0 Canberra 35.2809 149.1300 0.615768 2.602810 Canberra Cool temperate
1 Sydney 33.8688 151.2093 0.591122 2.639100 Sydney Warm temperate
2 Perth 31.9505 115.8605 0.557641 2.022147 Perth Warm temperate
localfinal = samplecityd.iloc[:,[0,-1]]
localfinal.head()
---
City climate
0 Canberra Cool temperate
1 Sydney Warm temperate
2 Perth Warm temperate
localfinal.columns = ['Location','Climate']
localfinal = localfinal.set_index(keys='Location')
# 在这里设定Localfinal的索引为地点,是为了之后进行map匹配
localfinal.head()
---
Climate
Location
Canberra Cool temperate
Sydney Warm temperate
Perth Warm temperate
Xtrain['Location'] = Xtrain['Location'].map(localfinal.iloc[:,0])
# 这里map中的localfinal.iloc[:,0]可以看做是一个字典,键是localfinal的键,值是localfinal.iloc[:,0]
# 从Xtrain['Location']取出键,在localfinal.iloc[:,0]中索引,找到对应值,也就是将气象站的名字替换成对应城市的气候
Xtrain['Location'].head()
---
0 High humidity summer, warm winter
1 Cool temperate
2 Mild temperate
3 Mild temperate
4 Hot dry summer, cool winter
Name: Location, dtype: object
# 观察上面的值,我们发现里面有逗号,两端还可能有空格,我们要去掉逗号和空格
Xtrain['Location'] = Xtrain['Location'].apply(lambda x:re.sub(',','',x.strip()))
# re.sub(想要替换的值,想要替换成什么,谁被替换)
# 做哑变量的时候就不能有逗号
# x.strip()去掉字符串两端的空格
Xtrain = Xtrain.rename(columns={"Location":'Climate'})
Xtest = Xtest.rename(columns={"Location":'Climate'})
到这里,地点就处理完毕了。其实,我们还没有将这个特征转化为数字,即还没有对它进行编码。我们稍后和其他的分类型变量一起来编码。