- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
一、前言
这周的任务添加了探索式数据分析(EDA),什么是探索式数据分析呢?
探索性数据分析(Exploratory Data Analysis,简称EDA)是一种数据处理方法,用于在数据建模之前对数据进行初步的观察和分析。通过可视化、总结统计量和数据变换等技术,探索性数据分析帮助研究者理解数据的特征、分布和潜在的关系,为后续的数据分析和建模提供指导。
二、导入数据
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Dropout
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.layers import Dropout
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.metrics import r2_score
from sklearn.metrics import mean_absolute_error , mean_absolute_percentage_error , mean_squared_error
data = pd.read_csv("weatherAUS.csv")
df = data.copy()
data.head()
pd.read_csv("weatherAUS.csv")
:read_csv
是Pandas库中的一个函数,用于读取以逗号分隔的值(CSV)文件格式的数据。它将CSV文件读取为一个DataFrame对象。"weatherAUS.csv"
是传递给read_csv
函数的参数,它指定了要读取的CSV文件的路径。如果文件位于当前工作目录中,则只需提供文件名即可。
df = data.copy()
:copy()
是DataFrame对象的一个方法,用于创建DataFrame的一个副本。df = data.copy()
将read_csv
函数读取的DataFrame对象data
复制一份,并将其赋值给变量df
。这样做的原因可能是不希望对原始数据进行修改,保留一个原始数据的备份。
data.head()
:head()
是DataFrame对象的一个方法,用于获取DataFrame的前几行数据。- 默认情况下,
head()
返回DataFrame的前5行,但它也可以接受一个参数来指定返回的行数。这通常用于快速查看数据集的前几行,以了解数据的结构和内容。
输出
data.describe()
data.describe()
是Pandas库中DataFrame对象的一个方法,它用于生成数据的快速统计摘要。这个方法会返回一个新的DataFrame,其中包含以下统计信息:
count
:非空值的数量。mean
:平均值。std
:标准差。min
:最小值。25%
:第一四分位数(Q1),即数据中所有数值由小到大排列后,位于25%位置的数值。50%
:中位数(Q2),即数据中所有数值由小到大排列后,位于中间位置的数值。75%
:第三四分位数(Q3),即数据中所有数值由小到大排列后,位于75%位置的数值。max
:最大值。
对于数值型的列(整数或浮点数),describe()
会计算上述所有统计量。对于非数值型的列(如字符串或对象),describe()
默认情况下只计算count
、unique
(唯一值的数量)、top
(出现频率最高的值)和freq
(最高频率)。
以下是一个示例输出,展示了describe()
方法的结果:
Column1 Column2
count 1000.000000 1000.000000
mean 50.546020 25.678947
std 15.432721 7.891234
min 1.000000 5.000000
25% 25.000000 20.000000
50% 50.000000 25.000000
75% 75.000000 30.000000
max 100.000000 50.000000
在这个例子中,Column1
和Column2
是数据集中的两个数值型列,describe()
为每列提供了统计摘要。
需要注意的是,describe()
默认不包括NaN值,如果需要包括NaN值,可以设置参数include='all'
。此外,可以通过参数percentiles
来指定特定的百分位数,或者通过参数exclude
来排除某些列。
data.dtypes
输出
以上输出显示了data
DataFrame中每一列的数据类型。以下是每个部分的含义:
-
列名:每一行左侧的名称代表DataFrame中的一列。例如,
Date
、Location
、MinTemp
等。 -
数据类型:每一行右侧的值表示相应列的数据类型。以下是常见的数据类型及其含义:
object
:通常用于存储字符串(文本)数据,也可以用于存储混合数据类型(如包含不同类型的数据的列表)。float64
:表示浮点数,通常用于存储小数和实数。int64
:表示整数,但在这个输出中没有显示。如果有整数类型的列,它们会显示为int64
。
具体到这个输出:
-
Date
和Location
列的数据类型是object
,这意味着这两列包含文本数据。通常,Date
列可能需要转换为日期时间格式(datetime
)以进行更有效的处理。 -
MinTemp
、MaxTemp
、Rainfall
、Evaporation
、Sunshine
、WindGustSpeed
、WindSpeed9am
、WindSpeed3pm
、Humidity9am
、Humidity3pm
、Pressure9am
、Pressure3pm
、Cloud9am
、Cloud3pm
、Temp9am
和Temp3pm
列的数据类型是float64
,这表明这些列包含浮点数,可能是与天气相关的测量值。 -
WindGustDir
、WindDir9am
、WindDir3pm
和RainToday
、RainTomorrow
列的数据类型是object
,这些列可能包含分类数据,例如风向或是否下雨的指示。
这个信息对于数据预处理非常重要,因为不同类型的数据可能需要不同的处理方法。例如,数值型数据可以进行数学运算,而文本数据可能需要转换为数值编码或进行其他形式的预处理。
data['Date'] = pd.to_datetime(data['Date'])
data['Date']
输出
这行代码的含义是:
- 它将DataFrame
data
中 ‘Date’ 列的数据类型从object
(通常是字符串)转换为datetime64
类型。 - 这样做的好处是,转换后的日期时间数据可以进行更复杂的时间序列操作,例如日期的加减、日期范围的选择、时间序列的重采样等。
- 此外,
datetime
对象在数据分析和可视化中通常更容易处理,因为它们可以自动按照时间顺序排序,并且可以方便地提取日期的各个组成部分(如年、月、日等)。
data['year'] = data['Date'].dt.year
data['Month'] = data['Date'].dt.month
data['day'] = data['Date'].dt.day
这三行代码分别从data
DataFrame中名为Date
的列(该列已经转换为datetime
类型)提取年份、月份和日期,并将它们作为新的列添加到DataFrame中。
以下是每行代码的解释:
data['year'] = data['Date'].dt.year
data['Date'].dt
访问Date
列的datetime
访问器,它允许你调用各种与日期和时间相关的属性和方法。.year
是一个属性,它返回Date
列中每个datetime
对象的年份部分。
data['Month'] = data['Date'].dt.month
- 同样,
.month
是一个属性,它返回Date
列中每个datetime
对象的月份部分。
- 同样,
data['day'] = data['Date'].dt.day
.day
是一个属性,它返回Date
列中每个datetime
对象的日期部分(即月份中的某一天)。
执行完这三行代码后,DataFramedata
将包含三个新的列:year
、Month
和day
,它们分别表示原始Date
列中的年份、月份和日期。这些新列可以用于进一步的数据分析和特征工程,例如根据年份或月份进行分组分析,或者创建新的时间相关的特征。
data.head()
输出
data.drop('Date',axis=1,inplace=True)
data.columns
这段代码由两行组成,每行的功能和目的如下:
data.drop('Date', axis=1, inplace=True)
drop
是Pandas DataFrame对象的一个方法,用于删除指定的行或列。axis=1
参数指定操作是在列(水平轴)上进行的。在Pandas中,axis=0
代表操作是在行(垂直轴)上进行的,而axis=1
代表操作是在列上进行的。inplace=True
是一个布尔参数,当设置为True
时,表示直接在原始的DataFrame上进行修改,而不是返回一个新的DataFrame。这意味着删除操作会改变data
DataFrame本身,而不是创建一个新的DataFrame来保存结果。
data.columns
columns
是DataFrame对象的一个属性,它返回DataFrame中所有列的名称。- 这行代码执行后,将返回一个包含
data
DataFrame所有列名称的Index对象。
结合这两行代码,第一行删除了名为Date
的列,第二行则返回了删除操作后DataFrame中剩余的所有列的名称。这通常用于确认列删除操作是否成功,或者用于查看DataFrame的最新结构。
输出
三、探索式数据分析(EDA)
1、数据相关性搜索
plt.figure(figsize=(15,13))
# data.corr()表示了data中的两个变量之间的相关性
ax = sns.heatmap(data.corr(), square=True, annot=True, fmt='2f')
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
plt.show()
这里我直接提取发现有报错,数据不能转化
错误信息 “could not convert string to float: ‘Albury’” 表示在尝试进行数值计算时,代码遇到了一个不能转换为浮点数的字符串 ‘Albury’。
随后将其修改
# 确定数值列
numerical_data = data.select_dtypes(include=[np.number])
# 计算数值列的相关性矩阵
corr_matrix = numerical_data.corr()
# 绘制热力图
plt.figure(figsize=(15, 13))
ax = sns.heatmap(corr_matrix, square=True, annot=True, fmt='2f')
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
plt.show()
select_dtypes(include=[np.number]) 用于选择所有数值类型的列。这样,在计算相关性时,只会考虑数值数据,从而避免类型转换错误
这段代码用于生成一个热力图(heatmap),显示变量之间的相关性。以下是代码中每个函数和参数的解释:
plt.figure(figsize=(30, 26))
plt.figure()
:这是matplotlib库中的一个函数,用于创建一个新的图形窗口。figsize=(30, 26)
:这是一个参数,用于设置图形窗口的大小,单位通常是英寸。这里设置为宽30英寸,高26英寸。
ax = sns.heatmap(corr_matrix, square=True, annot=True, fmt='2f')
sns.heatmap()
:这是seaborn库中的一个函数,用于绘制热力图,显示矩阵中数值的大小,通常用于展示相关性矩阵。corr_matrix
:这是传递给heatmap
函数的第一个参数,它代表一个矩阵,通常是DataFrame的相关性矩阵,计算变量之间的皮尔逊相关系数。square=True
:这是一个布尔参数,当设置为True
时,确保热力图中的每个单元格都是正方形。annot=True
:这是一个布尔参数,当设置为True
时,在每个单元格上显示数值(这里是相关系数)。fmt='2f'
:这是一个字符串参数,用于指定单元格内数值的格式。'2f'
表示以两位小数的形式显示浮点数。
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
ax.set_xticklabels()
:这是matplotlib坐标轴对象的一个方法,用于设置x轴刻度标签的文本。ax.get_xticklabels()
:这是matplotlib坐标轴对象的一个方法,用于获取x轴当前的刻度标签。rotation=90
:这是一个参数,用于设置x轴刻度标签的旋转角度,这里设置为90度,即水平显示。
输出
2.是否会下雨
sns.set(style="darkgrid")
plt.figure(figsize=(4,3))
sns.countplot(x='RainTomorrow', data=data)
这段代码使用了Seaborn和Matplotlib库来设置绘图风格并绘制一个计数图。
sns.set(style="darkgrid")
sns.set()
:这是Seaborn库中的一个函数,用于设置图表的样式。style="darkgrid"
:这是一个参数,用于指定绘图的整体风格。"darkgrid"
是Seaborn提供的一种风格,它为图表添加了一个深色背景和网格线,这使得图表元素更加清晰。
plt.figure(figsize=(4,3))
plt.figure()
:这是Matplotlib库中的一个函数,用于创建一个新的图形。figsize=(4,3)
:这是一个参数,用于设置图形的大小,这里的单位通常是英寸。这里设置图形的宽度为4英寸,高度为3英寸。
sns.countplot(x='RainTomorrow', data=data)
sns.countplot()
:这是Seaborn库中的一个函数,用于绘制计数图,它显示每个类别的数量。x='RainTomorrow'
:这是一个参数,指定了计数图要绘制的数据列。这里指定了data
DataFrame中的'RainTomorrow'
列。data=data
:这是一个参数,指定了要绘制的数据源。这里指定了变量data
,它应该是一个Pandas DataFrame。
总的来说,这段代码的目的是设置绘图风格为深色网格,创建一个新图形,并绘制一个计数图来显示data
DataFrame中'RainTomorrow'
列每个类别的数量。计数图通常用于显示分类变量的分布情况,在这里它显示了数据集中明天是否会下雨的分布。
输出
plt.figure(figsize=(4,3))
sns.countplot(x='RainToday', data=data)
输出
x=pd.crosstab(data['RainTomorrow'],data['RainToday'])
x
输出
y=x/x.transpose().sum().values.reshape(2,1)*100
y
这段代码是Python中的一个表达式,用于计算每列数据的百分比,并将结果存储在一个新的DataFrame中。
x=pd.crosstab(data['RainTomorrow'],data['RainToday'])
:pd.crosstab()
:这是Pandas库中的一个函数,用于创建交叉表。data['RainTomorrow']
和data['RainToday']
:这是从data
DataFrame中选择名为'RainTomorrow'
和'RainToday'
的列。crosstab
函数计算了'RainTomorrow'
列的每个类别(例如,‘Yes’ 或 ‘No’)与'RainToday'
列的每个类别(例如,‘Yes’ 或 ‘No’)的交叉频数。
y=x/x.transpose().sum().values.reshape(2,1)*100
:x.transpose()
:将x
转换为行数据。.sum()
:计算每行的总和,因为x
是交叉表,所以这里的总和实际上是每个类别组合出现的次数。.values.reshape(2,1)
:将结果转换为一个2行1列的数组。由于x
的列数是2,所以这里将结果转换为2行1列的数组。*100
:将每个值转换为百分比形式。
y
:- 最终,这个表达式返回了一个新列
y
,它包含了'RainTomorrow'
列的每个类别与'RainToday'
列的每个类别组合出现的百分比。
- 最终,这个表达式返回了一个新列
这段代码的正确性取决于'RainTomorrow'
和'RainToday'
列中的数据类型和值。如果这两个列都是分类变量,并且它们的数据类型是可以进行数学运算的,那么这段代码将正确地计算每个类别组合出现的百分比。
输出
如果今天不下雨,那么明天下雨的机会=53.22%
如果今天下雨明天下雨的机会=46.78%
y.plot(kind="bar",figsize=(4,3),color=['#006666','#d279a6']);
这段代码是Python中的一个表达式,用于绘制一个条形图。以下是代码的每个部分的解释:
y.plot(kind="bar",figsize=(4,3),color=['#006666','#d279a6'])
:y
:这是之前计算得到的DataFrame中的新列,它包含了'RainTomorrow'
列的每个类别与'RainToday'
列的每个类别组合出现的百分比。plot()
:这是Pandas DataFrame对象的一个方法,用于绘制图表。kind="bar"
:这是一个参数,用于指定要绘制的图表类型。在这里,它指定了要绘制一个条形图。figsize=(4,3)
:这是一个参数,用于设置图表的大小。这里的单位通常是英寸。这里设置图形的宽度为4英寸,高度为3英寸。color=['#006666','#d279a6']
:这是一个参数,用于指定图表中条形的颜色。这里指定了两种颜色,分别用于表示两个不同的类别。
输出
3.地理位置与下雨的关系
x=pd.crosstab(data['Location'],data['RainToday'])
# 获取每个城市下雨天数和非下雨天数的百分比
y=x/x.transpose().sum().values.reshape(-1, 1)*100
# 按每个城市的雨天百分比排序
y=y.sort_values(by='Yes',ascending=True)
color=['#cc6699','#006699','#006666','#862d86','#ff9966']
y.Yes.plot(kind="barh",figsize=(15,20),color=color)
这段代码使用了Pandas库中的crosstab
函数来创建一个交叉表,并使用Seaborn库中的plot
函数来绘制一个水平条形图,以展示每个城市下雨天数和非下雨天数的百分比。以下是代码的每个部分的解释:
x=pd.crosstab(data['Location'],data['RainToday'])
:pd.crosstab()
:这是Pandas库中的一个函数,用于创建交叉表。data['Location']
和data['RainToday']
:这是从data
DataFrame中选择名为'Location'
和'RainToday'
的列。crosstab
函数计算了'Location'
列的每个城市与'RainToday'
列的每个类别(例如,‘Yes’ 或 ‘No’)的交叉频数。
y=x/x.transpose().sum().values.reshape(-1, 1)*100
:x.transpose()
:将x
转换为行数据。.sum()
:计算每行的总和,因为x
是交叉表,所以这里的总和实际上是每个类别组合出现的次数。.values.reshape(-1, 1)
:将结果转换为一个列向量,这里使用-1
来指定列数,因为x
的行数是2(一个类别组合),所以结果是一个1行2列的数组。*100
:将每个值转换为百分比形式。
y=y.sort_values(by='Yes',ascending=True)
:y.sort_values()
:这是Pandas DataFrame对象的一个方法,用于对DataFrame中的数据进行排序。by='Yes'
:这是一个参数,用于指定排序的列。在这里,它指定了要根据'Yes'
列进行排序。ascending=True
:这是一个参数,用于指定排序的方向。在这里,它指定了升序排序。
color=['#cc6699','#006699','#006666','#862d86','#ff9966']
:- 这行代码定义了一个颜色列表,用于在条形图中为每个城市设置不同的颜色。
y.Yes.plot(kind="barh",figsize=(15,20),color=color)
:y.Yes
:这是y
DataFrame中的'Yes'
列,它包含了每个城市下雨天数的百分比。plot()
:这是Seaborn库中的一个函数,用于绘制图表。kind="barh"
:这是一个参数,用于指定要绘制的图表类型。在这里,它指定了要绘制一个水平条形图。figsize=(15,20)
:这是一个参数,用于设置图表的大小。这里的单位通常是英寸。这里设置图形的宽度为15英寸,高度为20英寸。color=color
:这是一个参数,用于指定图表中条形的颜色。这里指定了之前定义的颜色列表。
输出
4.湿度和压力对下雨的影响
data. columns
输出
plt.figure(figsize=(8,6))
sns.scatterplot(data=data,x='Pressure9am',y='Pressure3pm',hue='RainTomorrow');
输出
plt.figure(figsize=(8,6))
sns.scatterplot(data=data, x='Humidity9am', y='Humidity3pm', hue='RainTomorrow');
plt.figure(figsize=(8,6))
sns.scatterplot(x='MaxTemp', y= 'MinTemp', data=data, hue='RainTomorrow');
# 每列中缺失数据的百分比
data.isnull().sum() / data.shape[0] * 100
这段代码是Python中的一个表达式,用于计算DataFrame中每列缺失值的百分比。
data.isnull().sum()
:data.isnull()
:这是Pandas DataFrame对象的一个方法,用于生成一个布尔DataFrame,其中True表示缺失值,False表示非缺失值。.sum()
:这是对布尔DataFrame应用sum
方法,它会计算True值的总数,即每列中缺失值的数量。
data.shape[0]
:data.shape
:这是Pandas DataFrame对象的一个属性,用于获取DataFrame的形状,即行数和列数。[0]
:这是对形状数组取索引,这里取的是索引0,即行数。
data.isnull().sum() / data.shape[0] * 100
:- 这一步是将每列中缺失值的数量除以DataFrame的总行数,得到每列缺失值的百分比。
* 100
:这是将结果乘以100,以便将结果转换为百分比形式。
最终,这段代码的目的是计算DataFramedata
中每列缺失值的百分比,并将结果存储在一个新的DataFrame中。这样,您可以轻松地查看和比较每列中缺失值的百分比,以确定是否需要进行缺失值处理。
# 在该列中随机选择数进行填充
lst=['Evaporation','Sunshine','Cloud9am','Cloud3pm']
for col in lst:
fill_list = data[col].dropna()
data[col] = data[col].fillna(pd.Series(np.random.choice(fill_list, size=len(data.index))))
np.random.choice(fill_list, size=len(data.index)): 这个表达式使用了 NumPy 库的 random.choice() 函数,它从 fill_list 中随机选择值。size=len(data.index) 指定了选择的值的数量,它等于 DataFrame 的行数。这样,对于每个 NaN 值,都有一个从 fill_list 中随机选择的值作为填充。
pd.Series(…): np.random.choice() 返回一个 NumPy 数组,为了使其与 DataFrame 的列兼容,我们需要将其转换为一个 pandas Series。因此,pd.Series() 用于创建一个 Series 对象,它将用于填充 NaN 值。
data[col].fillna(…): 最后,fillna() 方法用于将 data[col] 中的所有 NaN 值替换为前面生成的随机选择的值。
整个代码段的目的就是将 data DataFrame 中指定列的所有 NaN 值用该列非 NaN 值的随机样本替换。这种方法通常用于在不破坏数据分布的情况下处理缺失数据,但它也有可能引入随机性,从而影响数据分析的结果。因此,在使用这种方法之前,应该仔细考虑其对数据分析的影响。
s = (data.dtypes == "object")
object_cols = list(s[s].index)
print(object_cols)
[‘MinTemp’, ‘MaxTemp’, ‘Rainfall’, ‘Evaporation’, ‘Sunshine’, ‘WindGustSpeed’, ‘WindSpeed9am’, ‘WindSpeed3pm’, ‘Humidity9am’, ‘Humidity3pm’, ‘Pressure9am’, ‘Pressure3pm’, ‘Cloud9am’, ‘Cloud3pm’, ‘Temp9am’, ‘Temp3pm’]
# .median(),中位数
for i in num_cols:
data[i].fillna(data[i].median(), inplace=True)
data.isnull().sum()
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
for i in object_cols:
data[i] = label_encoder.fit_transform(data[i])
x = data.drop(['RainTomorrow', 'day'],axis=1).values
y = data['RainTomorrow'].values
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.25,random_state=101)
scaler = MinMaxScaler()
scaler.fit(x_train)
x_train = scaler.transform(x_train)
x_test = scaler.transform(x_test)
from tensorflow.keras.optimizers import Adam
model = Sequential()
model.add(Dense(units=24, activation='tanh',))
model.add(Dense(units=18, activation='tanh'))
model.add(Dense(units=23, activation='tanh'))
model.add(Dropout(0.5))
model.add(Dense(units=12, activation='tanh'))
model.add(Dropout(0.2))
model.add(Dense(units=1, activation='sigmoid'))
optimizer = tf.keras.optimizers.Adam(learning_rate=2e-4)
model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics="accuracy")
early_stop = EarlyStopping(monitor='val_loss',
mode='min',
min_delta=0.001,
verbose=1,
patience=25,
restore_best_weights=True)
model.fit(x=x_train,
y=y_train,
validation_data=(x_test, y_test), verbose=1,
callbacks=[early_stop],
epochs = 10,
batch_size = 32
)
import matplotlib.pyplot as plt
acc = model.history.history['accuracy']
val_acc = model.history.history['val_accuracy']
loss = model.history.history['loss']
val_loss = model.history.history['val_loss']
epochs_range = range(10)
plt.figure(figsize=(14, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()