利用python进行大数据获取与数据预处理
1.概述
该实例针对北京公交数据进行处理与可视化。数据获取借助get_line_info函数等提取线路详情存为CSV。预处理设置字体、格式化日期后读取数据。清洗时剔除重复、异常、地铁相关数据,填充缺失值并归一化。可视化方面,公交线路上行站点数量分布直方图展现站点分布特点,前10运营公司线路数量柱状图经标签优化,清晰呈现各公司线路数量差异,助力公交运营研究。
2.设计内容
2.1数据获取阶段
-
编写函数用于获取单个公交线路的详细信息并整理成字典格式。
-
从网页源码中解析出公交线路的基本信息和站点信息。
-
存储所有线路信息,并将数据保存为CSV文件。
2.2数据预处理阶段
-
设置合适的字体属性以确保中文显示正常。
-
定义函数对日期时间列数据进行格式化处理。
-
读取CSV文件并处理编码问题,确保日期格式统一。
2.3数据清洗阶段
-
去除特定列中的重复数据,保留每个线路对应的最后一行数据。
-
检查数据的缺失值和唯一值情况,初步探索数据结构。
-
剔除与地铁相关的线路数据,确保数据集的准确性。
-
处理异常值,将超出合理范围的数据排除。
-
对数据进行归一化处理,提高数据可比性。
2.4数据可视化阶段
-
绘制公交线路上行站点数量分布的直方图,以便观察站点数量的分布情况。
3.设计方案
4.具体实现
4.1 数据获取
4.1.1 获取部分
定义get_line_info函数:该函数用于获取单个线路的详细信息并整理成字典格式。
(1)获取页面内容并解析
用requests获取页面文本,etree.HTML解析,xpath找相关div元素准备提取信息。
(2)提取线路基本信息并处理可能为空的值
初始化列表存字典,xpath提取起始站等信息,为空的运营时间设默认值。
(3)提取上下行站点信息
xpath找ul元素,初始化空列表遍历li提取站点名,获取上下行站点信息。
(4)整理信息为字典并添加到列表
4.1.2 数据抽取阶段
(1)获取网页源码:
(2)解析网页获取线路名称和url:
(3)存储所有线路信息:
(4)保存数据为CSV文件:
4.1.3 运行结果
4.2数据预处理部分
设置中文字体:为了在后续绘制的图表中正确显示中文,代码会查找系统中的SimHei
(黑体)字体,并将其设置为全局字体。
日期时间格式化:定义了一个函数format_date_column
,用于将数据中line_up_times
列的日期时间格式统一转换为"%Y-%m-%d %H:%M:%S"
。
读取数据:定义了一个函数read_data
,该函数会自动检测BeiJin_Bus_Line.csv
文件的编码,并尝试使用检测到的编码读取文件。如果自动检测失败,会依次尝试gbk
、gb2312
和latin1
编码。读取文件后,会调用format_date_column
函数对日期时间列进行格式化处理。
import pandas as pd
import chardet
import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import matplotlib as mpl
# 查找系统中SimHei字体的路径(你也可以手动查找后直接填写准确路径)
font_path = fm.findfont(fm.FontProperties(family='SimHei'))
# 设置字体属性,用于后续图表显示中文
font_prop = mpl.font_manager.FontProperties(fname=font_path)
# 设置全局字体
mpl.rcParams['font.family'] = font_prop.get_name()
# 定义函数用于格式化日期时间列数据,将可能不规范的格式严格统一为 "%Y-%m-%d %H:%M:%S"
def format_date_column(data):
# 先替换分隔符
data['line_up_times'] = data['line_up_times'].str.replace('/', '-')
# 再补全时间部分缺少的前导0等,确保格式符合 "%Y-%m-%d %H:%M:%S"
data['line_up_times'] = data['line_up_times'].apply(
lambda x: pd.to_datetime(x, format='%Y-%m-%d %H:%M', errors='coerce').strftime(
'%Y-%m-%d %H:%M:%S') if pd.notnull(x) else x)
return data
# 读取数据,先尝试检测文件编码并读取
def read_data():
try:
with open('BeiJin_Bus_Line.csv', 'rb') as f:
result = chardet.detect(f.read())
encoding = result['encoding']
ori_data = pd.read_csv('BeiJin_Bus_Line.csv', encoding=encoding)
except UnicodeDecodeError:
# 如果自动检测编码失败,尝试几种常见编码格式读取
encodings = ['gbk', 'gb2312', 'latin1']
for encoding in encodings:
try:
ori_data = pd.read_csv('BeiJin_Bus_Line.csv', encoding=encoding)
break
except UnicodeDecodeError:
continue
if 'ori_data' not in locals():
print("无法正确读取文件,请检查文件编码情况。")
raise
# 对日期时间列数据进行格式统一处理
ori_data = format_date_column(ori_data)
return ori_data
4.3数据清洗
重复数据剔除:提供两种方法剔除数据中的重复线路数据。
数据检查:查看数据的缺失值情况和每列的唯一值个数。
地铁线路删除:从数据中删除地铁线路相关数据。
异常值处理:通过分位数确定上下限,处理上行站点数量列的异常值。
数据归一化:对上行站点数量列进行归一化处理。
数据格式处理:统一线路名称为大写,去除运营公司列的前后空格。
特征提取:从日期时间列中提取年份和月份信息作为新列。
缺失值填充:使用众数填充线路价格列的缺失值。
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
# 定义函数用于处理重复数据剔除(公共逻辑提取)
def remove_duplicates(data):
data['line_up_times'] = pd.to_datetime(data['line_up_times'], format='%Y-%m-%d %H:%M:%S')
# 使用.loc进行索引,确保操作的是原始数据本身,而不是副本
data = data.sort_values(by=['line_name', 'line_up_times'], ascending=[True, True])
return data.drop_duplicates(subset=['line_name'], keep='last')
# 执行数据清洗与特征工程操作
def process_data(ori_data):
# 查看数据的缺失值情况
print(ori_data.isnull().sum())
# 查看每一列数据的唯一值个数
print(ori_data.nunique())
# 对原数据剔除重复的值 – 方法1
drop_dup_line1 = remove_duplicates(ori_data)
# 对原数据剔除重复的值 – 方法2
dup_data_all = ori_data[ori_data.duplicated(subset=['line_name'], keep=False)]
dup_data_all = remove_duplicates(dup_data_all)
other_data = ori_data[~ori_data['line_name'].isin(dup_data_all['line_name'])]
drop_dup_line2 = pd.concat([other_data, dup_data_all])
# 比较两种方法获得的结果线路是否一致
drop_dup_line2 = drop_dup_line2.sort_values(by=['line_name', 'line_up_times'], ascending=[True, True])
res = (drop_dup_line1['line_name'].values == drop_dup_line2['line_name'].values).sum()
print(res)
# 删除地铁线路
is_subway = drop_dup_line1['line_name'].str.contains('地铁', case=False) | drop_dup_line1['line_company'].str.contains(
'地铁', case=False)
subway_data = drop_dup_line1[is_subway]
# 获取删除地铁数据之后的全部数据
clean_data = drop_dup_line1[~drop_dup_line1['line_name'].isin(subway_data['line_name'])]
# 更合理地处理上行站点数量列的异常值,通过分位数确定上下限(示例,可根据实际调整)
q1 = clean_data['line_station_up_len'].quantile(0.05)
q3 = clean_data['line_station_up_len'].quantile(0.95)
iqr = q3 – q1
lower_bound = q1 – 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
clean_data = clean_data[
(clean_data['line_station_up_len'] > lower_bound) & (clean_data['line_station_up_len'] < upper_bound)]
# 提取要归一化的列数据
station_up_len_data = clean_data[['line_station_up_len']].values
# 创建归一化对象并进行归一化
scaler = MinMaxScaler()
normalized_station_up_len = scaler.fit_transform(station_up_len_data)
# 将归一化后的数据替换原列数据
clean_data['line_station_up_len'] = normalized_station_up_len
# 统一线路名称为大写
clean_data['line_name'] = clean_data['line_name'].str.upper()
# 去除运营公司列的前后空格
clean_data['line_company'] = clean_data['line_company'].str.strip()
# 提取年份和月份信息作为新列
clean_data['year'] = clean_data['line_up_times'].dt.year
clean_data['month'] = clean_data['line_up_times'].dt.month
print(len(clean_data))
# 查看数据集中的缺失值情况并填充
price_mode = clean_data['line_price'].mode()
if len(price_mode) > 0:
fill_value = price_mode[0]
# 使用.loc明确操作原始.data,避免SettingWithCopyWarning警告
clean_data.loc[:, 'line_price'] = clean_data['line_price'].fillna(fill_value)
print(clean_data.isnull().sum())
return clean_data
4.4 数据可视化
import pandas as pd
import matplotlib.pyplot as plt
# 执行数据可视化操作
def visualize_data(clean_data):
# 1. 绘制公交线路上行站点数量分布直方图
plt.figure(figsize=(10, 6))
clean_data['line_station_up_len'].hist(bins=20, color='skyblue', alpha=0.7) # 设置颜色和透明度
plt.xlabel('上行站点数量')
plt.ylabel('线路数量')
plt.title('北京公交线网上行站点数量分布情况')
plt.grid(axis='y', linestyle='–', alpha=0.7) # 添加y轴方向的网格线
plt.show()
# 2. 绘制不同运营公司的线路数量柱状图(只显示关键数据,取前10名运营公司)
company_counts = clean_data['line_company'].value_counts()
top_10_companies = company_counts.head(10) # 获取线路数量排名前10的运营公司数据
plt.figure(figsize=(10, 6))
ax = top_10_companies.plot(kind='bar', color='orange') # 设置柱状图颜色
ax.set_xlabel('运营公司')
ax.set_ylabel('线路数量')
ax.set_title('北京公交部分关键运营公司的线路数量分布')
ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right') # 调整x轴标签旋转及对齐方式
ax.grid(axis='y', linestyle='–', alpha=0.7) # 添加y轴方向的网格线
plt.show()
5.总结
数据获取阶段包括页面内容解析和信息提取,数据预处理阶段包括字体设置和数据格式化,数据清洗阶段包括重复值处理和异常值处理。最终,成功存储并清洗了公交线路数据,并展示了上行站点数量的数据分布。
作者:WWWxxx0424