当前位置:   article > 正文

python数据与挖掘实战学习:第四章数据预处理 笔记_python数据分析与挖掘实战 python数据预处理函数

python数据分析与挖掘实战 python数据预处理函数

前言

对原始数据中的异常值和缺失值进行数据清洗,完成后接着进行或同时进行数据集成、转换、规约等一系列的处理,该过程就是数据预处理。目的是提高数据的质量,并且要让数据更好地适应特定的挖掘技术或工具
数据预处理的主要内容包括数据清洗、数据集成、数据变换和数据规约


第四章 数据预处理

4.1 数据清洗

数据清洗主要是删除原始数据集中的无关数据、重复数据,平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值、异常值等。

4.1.1 缺失值处理

常用方法:删除记录、数据插补和不处理

插补方法方法描述
均值/中位数/众数插补根据属性值的类型,用该属性取值的平均数/中位数/众数进行插补
使用固定值将缺失的属性值用一个常量替换
最近临插补在记录中找到与缺失样本最接近的样本的该属性值插补
回归方法对带有缺失值的变量,根据已有数据和与其有关的其他变量(因变量)的数据建立拟合模型来预测缺失的属性值
插值法插值法是利用已知点建立合适的插值函数f(x),未知值由对应点xi求出的函数值f(xi)近似代替

删除记录虽然有效,但是具有很大的局限性,因为它是以减少历史数据来换取数据的完备,会造成资源的大量浪费,将丢弃了大量隐藏在这些记录中的信息。

用插值法来进行数据插补(前置知识铺垫:数值计算方法)
(1)拉格朗日插值法
对平面上已知的n个点(无两点在一条直线上)可以找到一个n-1次多项式
y=a0+a1x+a2x2+…+an-1xn-1,使此多项式曲线过这n个点。
因此拉格朗日插值多项式为:
拉格朗日插值多项式
插值基函数:当x=xj时,lj(xj)=1;当x!=xj时,lj(xj)=0

拉格朗日插值基函数
将缺失的函数值对应的点x代入插值多项式得到缺失值的近似值L(x)。

当插值节点增减时,插值多项式就会随之变化,因此提出牛顿插值法。

(2)牛顿插值法
对已知的n个点有差商公式:
参考牛顿插值法差商公式
牛顿插值多项式
将缺失的函数值对应的点x代入插值多项式得到缺失值的近似值f(x)。

但Scipy库中,只提供了拉格朗日插值法的函数,对于牛顿插值法需要自行编写函数。

import pandas as pd
from scipy.interpolate import lagrange  # 导入拉格朗日插值函数

inputfile = './demo/data/catering_sale.xls'  # 销量数据路径
outpitfole = './demo/tmp/sales.xlsx'  # 输出数据路径,python版本问题这里的文件后缀需要改为xlsx

data = pd.read_excel(inputfile)  # 读入数据
x = data.copy()
# print(x)
x.loc[:, '销量'][(x.loc[:, '销量'] < 400) | (x.loc[:, '销量'] > 5000)] = None  # 过滤异常值,将其变为空值
print(x)


# 自定义列向量插值函数
# s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
def ployinterp_column(s, n, k=5):
    y = s[list(range(n-k, n))+list(range(n+1, n+1+k))]  # 取n前后k个未缺失的值
    y = y[y.notnull()]  # 剔除空值
    return lagrange(y.index, list(y))(n)  # 插值并返回插值结果


print(x.loc[1, '销量'])
# 逐个元素判断是否需要插值
for j in range(len(x)):
    if (x['销量'].isnull())[j]:  # 如果是空值就插值
        x.loc[j, '销量'] = ployinterp_column(x['销量'], j)


x.to_excel(outpitfole)  # 输出结果写入文件

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

这里修改了部分书上的代码,但输出文件中日期列是一堆井字符,我直接将原文件的日期列复制到输出文件中了。

4.1.2 异常值处理

在数据预处理时,异常值的剔除需要视具体情况而定,要先分析异常值出现可能原因,再判断异常值是否应该舍弃,如果是正确的数据,可以直接在具有异常值的数据集上进行挖掘建模。
异常值处理常用方法:

异常值处理方法方法描述
删除含有异常值的记录直接将含有异常值的记录删除
视为缺失值将异常值视为缺失值,利用缺失值处理的方法进行处理
平均值修正可用前后两个观测值的平均值修正该异常值
不处理直接在具有异常值的数据集上进行挖掘建模

4.2 数据集成

数据挖掘需要的数据往往分布在不同的数据源中,数据集成就是将多个数据源合并存放在一个一致的数据存储(如数据仓库)中的过程。
来自多个数据源的现实世界实体的表达形式不一样,有可能不匹配,要考虑到实体识别问题属性冗余问题,从而将源数据在最低层上加以转换、提炼和集成。

4.2.1 实体识别

实体识别是指从不同数据源识别出现实世界的实体,它的任务是统一不同源数据的矛盾之处
(1)同名异义:两个数据源具有相同的属性名,但描述的是不同实体。
(2)异名同义:两个数据源具有不同的属性名,但描述的是同一实体。
(3)单位不统一:描述同一个实体分别用的是国际单位和中国传统的计量单位。

4.2.2 冗余属性识别

数据集成往往导致数据冗余:
1)同一属性多次出现;
2)同一属性命名不一致导致重复。
对于冗余属性要先分析,检测到后再将其删除。对有些冗余属性可以用相关性分析检测。给定两个数值型的属性A和B,根据其属性值,用相关系数度量一个属性在多大程度上蕴含另一个属性。

4.3 数据变换

数据变换主要是对数据进行规范化处理,将数据转换成“适当的”形式,以适用于挖掘任务及算法的需要

4.3.1 简单函数变换

简单函数变换是对原始数据进行某些数学函数变换,常用的变换包括平方、开方、取对数、差分运算等。
简单函数变换常用来将不具有正态分布的数据变换为具有正态分布的数据;在时间序列中,对数变换或差分运算可以将非平稳序列转换成平稳序列;对大区间的数据使用对数变换对其进行压缩可以便于计算。

4.3.2 规范化

为了消除指标之间的量纲和取值范围差异的影响,需要进行标准化处理,将数据按照比例进行缩放,使之落入一个特定的区域,便于进行综合分析。
(1)最小-最大规范化
最小-最大规范化也称为离差标准化,是对原始数据的线性变换,将数值映射到[0,1]之间。
x ∗ = x − m i n m a x − m i n x^{*}=\frac{x-min}{max-min} x=maxminxmin
该方法是最简单的规范化方法,但如果数值集中且某个数值很大,则规范化后各值会接近于0,并且将会相差不大。
(2)零-均值规范化
零-均值规范化也称标准差标准化,经过处理的数据的均值为0,标准差为1.
x ∗ = x − x ‾ σ x^{*}=\frac{x-\overline{x}}{σ} x=σxx
其中 x ‾ \overline{x} x为原始数据的均值,σ为原始数据的标准差。
(3)小数定标规范化
通过移动属性值的小数位数,将属性值映射到[-1,1]之间,移动的小数位数取决于属性值绝对值的最大值。
x ∗ = x 1 0 k x^{*}=\frac{x}{10^{k}} x=10kx

import pandas as pd
import numpy as np

datafile = './demo/data/normalization_data.xls'  # 参数初始化
data = pd.read_excel(datafile, header=None)  # 读取数据
data1 = (data - data.min())/(data.max() - data.min())  # 最小-最大规范化
data2 = (data - data.mean())/data.std()  # 零-均值规范化
data3 = data/10**np.ceil(np.log10(data.abs().max()))  # 小数定标规范化
print(data)
print(data1)
print(data2)
print(data3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.3.3 连续属性离散化

对于某些数据挖掘算法(如分类算法),要求数据是分类属性形式。因此需要将连续属性变换成分类属性,即连续属性离散化。
1.离散化的过程
连续属性的离散化就是在数据的取值范围内设定若干个离散的划分点,将取值范围划分为一些离散化的区间,最后用不同的符号或整数值代表落在每个子区间中的数据值。
即离散化涉及两个子任务:确定分类数以及如何将连续属性值映射到这些分类值
2.常用的离散化方法
(1)等宽法
将属性的值域分成具有相同宽度的区间,区间的个数由数据本身的特点决定,或者由用户指定,类似于制作频率分布表。
(2)等频法
将相同数量的记录放进每个区间。

这两个方法都需要人为规定划分区间的个数。
并且等宽法对离群点比较敏感,倾向于不均匀地把属性值分布到各个区间。而等频法即使避免了这个问题,但可能将相同的数据值分到不同的区间以满足每个区间中固定的数据个数。

(3)基于聚类分析的方法
一维聚类的方法包括两个步骤,首先将连续属性的值用聚类算法进行聚类,然后再将聚类得到的簇进行处理,合并到一个簇的连续属性值并做同一标记,也需要客户指定簇的个数,从而决定产生的区间数。

import pandas as pd

datafile = './demo/data/discretization_data.xls'  # 参数初始化
data = pd.read_excel(datafile)
data = data[u'肝气郁结证型系数'].copy()
k = 4

d1 = pd.cut(data, k, labels = range(k))  # 等宽离散化,各个类比依次命名为0,1,2,3
# print(d1)

# 等频率离散化
w = [1.0*i/k for i in range(k+1)]
w = data.describe(percentiles = w)[4:4+k+1]  # 使用describe函数自动计算分位数,并将数值存放在w中
# print(w)
w[0] = w[0]*(1-1e-10)  # 这一步不太理解
# print(w)
d2 = pd.cut(data, w, labels=range(k))
# print(d2)


from sklearn.cluster import KMeans  # 导入kmeans
kmodel = KMeans(n_clusters=6)  # 建立模型,为了后面的代码分成4类,这里的簇=6
kmodel.fit(data.values.reshape((len(data), 1)))  # 训练模型
c = pd.DataFrame(kmodel.cluster_centers_).sort_values(0)  # 输出聚类中心,并且排序(默认是随机序的)
print(c)
w = c.rolling(2).mean().iloc[1:]  # 相邻两项求中点,作为边界点
d3 = pd.cut(data, w[0], labels=range(k))



def cluster_plot(d, k):  # 自定义作图函数来显示聚类结果
    import matplotlib.pyplot as plt
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
    plt.figure(figsize=(8, 3))
    for j in range(0, k):
        plt.plot(data[d == j], [j for i in d[d == j]], 'o')

    plt.ylim(-0.5, k-0.5)
    return plt


cluster_plot(d1, k).show()
cluster_plot(d2, k).show()
cluster_plot(d3, k).show()  # 聚类的分出来只有两个类
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

4.3.4 属性构造

在数据挖掘的过程中,为了提取更有用的信息,挖掘更深层次的模式,提高挖掘结果的精度,需要利用已有的属性集构造出新的属性,并加入到现在的属性集合中。
如以下的防窃漏电诊断建模,在已有属性供入电量和供出电量的条件下,为了判断是否有大用户存在窃漏电行为,构造一个新指标——线损率。

import pandas as pd
inputfile = './demo/data/electricity_data.xls'
outputfile = './demo/tmp/electricity_data.xlsx'

data = pd.read_excel(inputfile)
# print(data)
data['线损率'] = (data['供入电量']-data['供出电量'])/data['供入电量']  # 计算线损率
# print(data)
data.to_excel(outputfile, index=False)  # 写入输出文件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.3.5 小波变换

小波变换是一种信号分析手段,具有多分辨率的特点,在时域和频域都具有表征信号局部特征的能力,通过伸缩和平移等运算过程对信号进行多尺度的聚焦分析,提供一种非平稳信号的时频分析手段,可以由粗及细地逐步观察信号,从中提取有用信息。

时域是以时间为横坐标,时域分析就是观察一件事物随时间的变化规律。
频域则是以频率为横坐标,频域分析(频谱分析)就是在观察一件事物在不同频率上的分布情况

能刻画某个问题的特征量往往是隐含在一个信号中的某个或某些分量中,小波变换可以把非平稳信号分解为表达不同层次、不同频带信息的数据序列,即小波系数选取适当的小波系数,即完成了信号的特征提取
(1)基于小波变换的特征提取方法

基于小波变换的特征提取方法方法描述
基于小波变换的多尺度空间能量分布特征提取方法各尺度空间内的平滑信号和细节信号能提供原始信号的时频局域信息,特别是能提供不同频段上信号的构成信息。把不同分解尺度上的信号的能量求解出来,就可以将这些能量尺度顺序排列,形成特征向量供识别用
基于小波变换的多尺度空间的模极大值特征提取方法利用小波变换的信号局域化分析能力,求解小波变换的模极大值特性来检测信号的局部奇异性,将小波变换模极大值的尺度参数s、平移参数t及其幅值作为目标的特征量
基于小波包变换的特征提取方法利用小波分解,可将时域随机信号序列映射为尺度域各子空间内的随机系数序列,按小波包分解得到的最佳子空间内随机系数序列,按小波包分解得到的最佳子空间内随机系数序列的不确定性程度最低,将最佳子空间的熵值及最佳子空间在完整二叉树中的位置参数作为特征量,可以用于目标识别
基于适应性小波神经网络的特征提取方法基于适应性小波神经网络的特征提取方法可以把信号通过分析小波拟合表示,进行特征提取

(2)小波基函数
小波基函数是一种具有局部支集的函数,并且平均值为0,小波基函数满足 ψ ( 0 ) = ∫ ψ ( t ) d t = 0 ψ(0)=\int_{}ψ(t)dt=0 ψ(0)=ψ(t)dt=0。常用的小波基有Haar小波基、db系列小波基等。
(3)小波变换
对小波基函数进行伸缩和平移变换:
ψ a , b ( t ) = 1 ∣ a ∣ ψ ( t − b a ) ψ_{a,b}(t) = \frac{1}{\sqrt{|a|}}ψ(\frac{t-b}{a}) ψa,b(t)=a 1ψ(atb)
其中,a为伸缩因子,b为平移因子。
任意函数f(t)的连续小波变换(CWT)为:
W f ( a , b ) = 1 ∣ a ∣ ∫ f ( t ) ψ ( t − b a ) d t W_{f}(a,b) = \frac{1}{\sqrt{|a|}}\int_{}f(t)ψ(\frac{t-b}{a})dt Wf(a,b)=a 1f(t)ψ(atb)dt
连续小波变换为f(t)->Wf(a,b)的映射,对小波基函数ψ(t)增加约束条件 C ψ = ∫ ∣ ψ 2 ( t ) ∣ 2 / t d t < ∞ C_{ψ}=\int_{}|ψ_2(t)|^{2}/t dt < \infty Cψ=ψ2(t)2/tdt<,就可以由 W f ( a , b ) W_{f}(a,b) Wf(a,b)逆变换得到f(t),其中 ψ 2 ( t ) ψ_2(t) ψ2(t)为ψ(t)的傅里叶变换。

傅里叶变换:任何连续周期信号都可以由一组适当的正弦曲线组合而成。虽然正弦曲线无法组合成一个带有棱角的信号。但是,我们可以用正弦曲线来非常逼近地表示它,逼近到两种表示方法不存在能量差别。

其逆变换为:
f ( t ) = 1 C ψ ∫ ∫ 1 / a 2 W f ( a , b ) ψ ( t − b a ) d a ∗ d b f(t)=\frac{1}{C_{ψ}}\int\int 1/a^{2}W_{f}(a,b)ψ(\frac{t-b}{a})da*db f(t)=Cψ11/a2Wf(a,b)ψ(atb)dadb
(4)基于小波变换的多尺度空间能量分布特征提取方法
该方法是对信号进行频带分析,再分别以计算所得的各个频带的能量作为特征向量。
信号f(t)的二进小波分解可表示为:
f ( t ) = A j + ∑ D j f(t)=A^{j}+\sum D^{j} f(t)=Aj+Dj
其中A是近似信号,为低频部分;D是细节信号,为高频部分。
频带分布:在这里插入图片描述
信号的总能量为:
E = E A j + ∑ E D j E = EA_{j}+\sum ED_{j} E=EAj+EDj
选择第j层的近似信号和各层的细节信号的能量作为特征,构造特征向量:
F = [ E A j , E D 1 , E D 2 , . . . , E D j ] F=[EA_{j},ED_{1},ED_{2},...,ED_{j}] F=[EAj,ED1,ED2,...,EDj]
利用小波变换可以对声波信号进行特征提取,提取出可以代表声波信号的向量数据,即完成从声波信号到特征向量数据的变换。

在python中对信号处理更全面的库是PyWavelets(pywt)库,安装时是PyWavelets库,导入的是pywt库。

from scipy.io import loadmat  # 导入loadmat函数读取mat文件
mat = loadmat('./demo/data/leleccum.mat')
# print(mat)
signal = mat['leleccum'][0]  # 一系列的信号数组
# print(signal)

import pywt
coeffs = pywt.wavedec(signal, 'bior3.7', level=5)
# 返回结果为level+1个数字,第一个数组为逼近系数数组,后面的依次是细节系数数组
print(coeffs)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

信号部分属实是知识盲区,以后要处理信号数据时再恶补知识

4.4 数据规约

在大数据集上进行复杂的数据分析和挖掘需要很长的时间,数据规约产生更小但保持数据完整性的新数据集。在规约后的数据集上进行分析和挖掘将更有效率。

4.4.1 属性规约

属性规约通过属性合并来创建新属性维度,或者直接通过删除不相关的属性(维)来减少数据维数(数据降维),从而提高数据挖掘的效率、降低计算成本。目标是寻找出最小的属性子集并确保新数据子集的概率分布尽可能地接近原来数据集的概率分布
属性规约常用方法:

属性规约方法方法描述
合并属性将一些旧属性合为新属性
逐步向前选择从一个空属性集开始,每次从原来属性集合中选择一个当前最优的属性添加到当前属性子集中。直到无法选择出最优属性或满足一定阈值约束为止
逐步向后删除从一个全属性集开始,每次从当前属性子集中选择一个当前最差的属性并将其从当前属性子集中消去。直到无法选择出最差属性或满足一定阈值约束为止
决策树归纳利用决策树的归纳方法对初始数据进行分类归纳学习,获得一个初始决策树,所以没有出现在这个决策树上的属性均可认为是无关属性,因此将这些属性从初始集合中删除,就可以获得一个较优的属性子集
主成分分析用较少的变量去解释原始数据中的大部分变量,即将许多相关性很高的变量转化成彼此相互独立或不相关的变量

逐步向前选择、逐步向后删除和决策树归纳是属于直接删除不相关属性(维)方法。主成分分析是一种用于连续属性的数据降维方法,通常选出比原始变量个数少,能解释大部分数据中的变量的几个新变量,即主成分(通常主成分的信息贡献率能达到80%以上),来代替原始变量进行建模。

主成分分析的计算步骤:
1)设原始变量X1,X2,…,Xp的n次观测数据矩阵为:
观测数据矩阵
2)将数据矩阵按列进行中心标准化。
3)求相关系数矩阵R,R=(rij)p*p,rij的定义为:
相关系数矩阵
其中,rij=rji,rii=1。
4)求R的特征方程det(R-λE)=0的特征根λ1>=λ2>=λp>0
5)确定主成分个数m: ∑ i = 1 m λ i ∑ i = 1 p λ i > = α \frac{\sum_{i=1}^{m}λ_{i}}{\sum_{i=1}^{p}λ_{i}}>=α i=1pλii=1mλi>=α,α根据实际问题确定,一般取80%。
6)计算m个相应的单位特征向量:
特征向量
7)计算主成分:
Z i = β 1 i X 1 + β 2 i X 2 + . . . + β 2 i X 2 , i = 1 , 2 , . . . , m Z_{i}=β_{1i}X_{1}+β_{2i}X_{2}+...+β_{2i}X_{2},i=1,2,...,m Zi=β1iX1+β2iX2+...+β2iX2i=1,2...m
在python中,主成分分析法的函数位于Scikit-Learn下:
sklearn.decomposition.PCA(n_components=None,copy=True,whiten=False)
参数说明:
(1)n_components
PCA算法中所要保留的主成分个数n,即保留下来的特征个数n,数值类型可为int或string。
(2)copy
表示是否在运行算法时,将原始训练数据复制一份,数值类型为bool。
(3)whiten
白化,使得每个特征具有相同的方差,数值类型为bool。

import pandas as pd

inputfile = './demo/data/principal_component.xls'
outputfile = './demo/tmp/dimention_reducted.xlsx'  # 储存降维后的数据

data = pd.read_excel(inputfile, header=None)
# print(data)
from sklearn.decomposition import PCA
pca = PCA()
pca.fit(data)
p1 = pca.components_  # 返回模型的各个特征向量
p2 = pca.explained_variance_ratio_  # 返回各个成分各自的方差百分比
print(p1)
print(p2)

# 前四个主成分的贡献率分别为77.4%,15.69%,4.28%,2.4%,已达到99.77% ,因此选取前三个足以
pca = PCA(3)
pca.fit(data)
low_d = pca.transform(data)  # 用它降低维度
pd.DataFrame(low_d).to_excel(outputfile)  #保存结果
pca.inverse_transform(low_d)  # 必要时可以用inverse_transform()函数来复原数据
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.4.2 数值规约

数值规约指通过选择替代的、较小的数据来减少数据量,包括有参数方法无参数方法
有参数方法是使用一个模型来评估数据,只需存放参数,而不需要存放实际数据,例如回归和对数线性模型。
无参数方法需要存放实际数据,例如直方图、聚类、抽样。
(1)直方图
直方图使用分箱来近似数据分布,属性A的直方图将A的数据分布划分为不相交的子集或桶,如果每个桶只代表单个属性值/频率对,则该桶称为单桶。
(2)聚类
聚类将数据元组视为对象,将对象划分为簇,使一个簇中的对象相互“相似”,而与其他簇中的对象“相异”。
(3)抽样
抽样用比原始数据小得多的随机样本(子集)表示原始数据集。抽样方法有:s个样本无放回简单随机抽样,s个样本有放回简单随机抽样,聚类抽样,分层抽样。
抽样最常用来估计聚集查询的结果,在指定的误差范围内,可以确定(使用中心极限定理)估计一个给定的函数所需的样本大小。
(4)参数回归
简单线性模型和对数线性模型可以用来近似描述给定的数据。
对简单线性模型的单个预测变量x有y=kx+b,其中k和b可以用最小二乘求解。
对数线性模型:用来描述期望频数与协变量(指与因变量有线性相关并在探讨自变量与因变量关系时通过统计技术加以控制的变量)之间的关系。
lnm = β01x1 +…+βkxk,一般用来近似离散的多维概率分布也可以用与维规约和数据光滑。

4.5 Python主要数据预处理函数

函数名函数功能
interpolate一维、高维数据插值(Scipy)
unique去除数据中的重复元素,得到单值元素列表,它是对象的方法名(pandas/numpy)
isnull判断是否空值(pandas)
notnull判断是否非空值(pandas)
PCA对指标变量矩阵进行主成分分析(Scikit-Learn)
random生成随机矩阵(numpy)
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号