入门数据挖掘(二手车交易价格预测案例)-第一部分:赛题理解和EDA


写在前面
这是一个由Datawhal与天池联合发起的0基础入门系列赛事。(比赛地址: 零基础入门数据挖掘 - 二手车交易价格预测)。借Datawhal举办的这次活动,也让我这个从没参加过比赛的小白有个入门的机会,在此非常感谢Datawhal。
此次是基于二手车交易价格预测的比赛进行数据挖掘比赛的赛题理解和数据探索性分析的学习。学习资料由AI蜗牛车 车哥总结提供,资料非常详细,这篇也基本是按照车哥的资料来进行的,也非常感谢车哥。
下面让我们一起进入数据挖掘竞赛的世界第一篇!

1.赛题理解

一般理解赛题有以下四步:题目理解→数据理解→预测指标→赛题分析。
1.题目理解:比赛要求预测二手汽车的交易价格,这是一个典型的回归问题。
2.数据理解:数据来自某交易平台的二手车交易记录,总数据量超过40w,包含31列变量信息,其中15列为匿名变量。下面我么来看看数据集的字段信息。
数据字段
了解列的性质会有助于我们对于数据的理解和后续分析。 Tip:匿名特征,就是未告知数据列所属的性质的特征列。
然后就是了解一下数据量,,根据数据量也要考虑电脑的配置和后面数据处理的难度和时间。
3.预测目标:也就是根据前面的了解,初步了解一下我们要用什么指标去评价模型的好坏。
本赛题的评价标准为MAE(Mean Absolute Error):
M A E = ∑ i = 1 n ∣ y i − y ^ i ∣ n MAE=\frac{\sum_{i=1}^{n}\left|y_{i}-\hat{y}_{i}\right|}{n} MAE=ni=1nyiy^i
其中 y i y_{i} yi代表第i个样本的真实值,其中 y ^ i \hat{y}_{i} y^i代表第i个样本的预测值。
下面说一下一般问题的评估指标
分类问题的评价指标

  • 二分类器算法:评价指标主要有准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1值、pr曲线、ROC-AUC曲线。
  • 多分类算法:评价指标主要有accuracy, 宏平均和微平均,F-score。
    具体可参考:一文读懂分类算法常用评价指标

回归预测类的评价指标

  • 平均绝对误差(Mean Absolute Error,MAE),均方误差(Mean Squared Error,MSE),平均绝对百分误差(Mean Absolute Percentage Error,MAPE),均方根误差(Root Mean Squared Error), R2(R-Square)
    具体公式及Python实现可参考学习:回归预测的评价指标

4.赛题分析:此题为典型的回归问题。通过数据科学以及机器学习深度学习的办法来进行建模得到结果。主要应用xgb、lgb、catboost,以及pandas、numpy、matplotlib、seabon、sklearn、keras等等数据挖掘常用库或者框架来进行数据挖掘任务。
总结:赛题理解是极其重要的,对于赛题的理解甚至会影响后续的特征工程构建以及模型的选择,最主要是会影响后续发展工作的方向,比如挖掘特征的方向或者存在问题解决问题的方向,对了赛题背后的思想以及赛题业务逻辑的清晰,也很有利于花费更少时间构建更为有效的特征模型。

2. 数据探索性分析

2.1 EDA目标

数据探索性分析的主要目标:

  • 熟悉数据集,了解数据集,对数据集进行验证来确定所获得数据集可以用于接下来的机器学习或者深度学习使用。
  • 当了解了数据集之后我们下一步就是要去了解变量间的相互关系以及变量与预测值之间的存在关系。

2.2 EDA一般流程

下面看一下EDA的主要流程:
(1)载入各种数据科学以及可视化库:

  • 数据科学库 pandas、numpy、scipy;
  • 可视化库 matplotlib、seabon;
  • 其他;

(2)载入数据:

  • 载入训练集和测试集;
  • 简略观察数据(head()+shape);

(3)数据总览:

  • 通过describe()来熟悉数据的相关统计量
  • 通过info()来熟悉数据类型

(4)判断数据缺失和异常

  • 查看每列的存在nan情况
  • 异常值检测

(5)了解预测值的分布

  • 总体分布概况(无界约翰逊分布等)
  • 查看skewness and kurtosis
  • 查看预测值的具体频数

(6)特征分为类别特征和数字特征,并对类别特征查看unique分布
(7)数字特征分析:

  • 相关性分析
  • 查看几个特征得 偏度和峰值
  • 每个数字特征得分布可视化
  • 数字特征相互之间的关系可视化
  • 多变量互相回归关系可视化

(8)类型特征分析

  • unique分布
  • 类别特征箱形图可视化
  • 类别特征的小提琴图可视化
  • 类别特征的柱形图可视化类别
  • 特征的每个类别频数可视化(count_plot)

(9)用pandas_profiling生成数据报告

2.3 本次比赛的EDA代码

因EDA涉及大量可视化图片,就不一一展示在本文中。本文代码基于Jupter Notebok。可下载相关数据集自己跑一遍,印象深刻,有助于理解。

2.3.1 导入所需库,载入数据

导入各种数据科学以及可视化库

import warnings  #利用过滤器来实现忽略警告语句
warnings.filterwarnings('ignore')

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno  #缺失值可视化

加载数据

## 1) 载入训练集和测试集;
path = './'
Train_data = pd.read_csv(path+'used_car_train_20200313.csv', sep=' ')
Test_data = pd.read_csv(path+'used_car_testA_20200313.csv', sep=' ')

2.3.2 总览数据概况

简略观察下数据,数据的shape

Train_data.head().append(Train_data.tail())

在这里插入图片描述

Test_data.head().append(Test_data.tail())

在这里插入图片描述
查看数据的shape大小,使得自己对数据有个了解,方便后面处理。

Train_data.shape
# (150000, 31)
Test_data.shape
# (50000, 30)

使用describe查看每列的统计量,个数count、平均值mean、方差std、最小值min、中位数25% 50% 75% 、以及最大值 看这个信息主要是瞬间掌握数据的大概的范围以及每个值的异常值的判断,比如有的时候会发现999 9999 -1 等值这些其实都是nan的另外一种表达方式,有的时候需要注意下。

Train_data.describe()

在这里插入图片描述

Test_data.describe()

在这里插入图片描述
通过info来了解数据每列的type,有助于了解是否存在除了nan以外的特殊符号异常。

# 通过info()来熟悉数据类型
Train_data.info()

在这里插入图片描述

Test_data.info()

在这里插入图片描述

2.3.3 判断数据缺失和异常

查看每列的缺失值情况

## 1) 查看每列的存在nan情况
Train_data.isnull().sum()

在这里插入图片描述

Test_data.isnull().sum()

在这里插入图片描述
可以看到,在训练集和测试集中,bodyType、fuelType、gearbox这个三个特征的缺失值比较严重。
下面对缺失值情况可视化

# nan可视化
missing = Train_data.isnull().sum()
missing = missing[missing > 0]   # 只选择与缺失值的列
missing.sort_values(inplace=True)   #将缺失值从小到大排列
missing.plot.bar()   # 画柱状图

在这里插入图片描述
通过以上两句可以很直观的了解哪些列存在 “nan”, 并可以把nan的个数打印,主要的目的在于 nan存在的个数是否真的很大,如果很小一般选择填充,如果使用lgb等树模型可以直接空缺,让树自己去优化,但如果nan存在的过多、可以考虑删掉。
下面通过missingno将缺失值可视化

# 可视化看下缺省值
msno.matrix(Train_data.sample(250))   #随机采样250个

在这里插入图片描述

msno.bar(Train_data.sample(1000))  #随机采样10000个

在这里插入图片描述

# 可视化看下缺省值
msno.matrix(Test_data.sample(250))

在这里插入图片描述

msno.bar(Test_data.sample(1000))

在这里插入图片描述
从上面可以看出,测试集的缺省和训练集的差不多情况, 可视化有三列有缺省,fuelType缺省得最多。
但是,特别注意,这样就把所有缺省值找到了吗?在数据中,什么样数据都有可能出现,也就是一些异常值,比如有些特殊符号。
让我们回头看一下Train_data.info()打印的信息,会发现,除了notRepairedDamage 为object类型其他都为数字,让我们来一下notRepairedDamage字段的object类型有哪些。

#查看notRepairedDamage 的字段所有object类型并统计数量
Train_data['notRepairedDamage'].value_counts()
# 结果如下
0.0    111361
-       24324
1.0     14315
Name: notRepairedDamage, dtype: int64

可以看出来‘ - ’也其实为空缺值。因为很多模型对nan有直接的处理,这里我们先不做处理,先替换成nan。

Train_data['notRepairedDamage'].replace('-', np.nan, inplace=True)

然后我们再看一下数据情况:

Train_data['notRepairedDamage'].value_counts()
# 结果如下
0.0    111361
1.0     14315
Name: notRepairedDamage, dtype: int64

可以看到,notRepairedDamage列已经没有’-'了。下面再看看总体的缺省情况:

Train_data.isnull().sum()

在这里插入图片描述
然后对测试集做同样的处理:

Test_data['notRepairedDamage'].value_counts()
# 结果如下
0.0    37249
-       8031
1.0     4720
Name: notRepairedDamage, dtype: int64
# 替换
Test_data['notRepairedDamage'].replace('-', np.nan, inplace=True)

最后,还有一些特征会出现严重倾斜,一般不会对预测有什么帮助,故这边先删掉,当然也可以继续挖掘,但是一般意义不大。比如下面两个:

Train_data["seller"].value_counts()
#  结果如下
0    149999
1         1
Name: seller, dtype: int64
Train_data["offerType"].value_counts()
#  结果如下
0    150000
Name: offerType, dtype: int64

直接删掉这两列

del Train_data["seller"]
del Train_data["offerType"]
del Test_data["seller"]
del Test_data["offerType"]

2.3.4 了解预测值的分布

预测值即二手车的价格。首先看一下大概的情况:

Train_data['price'].value_counts()

在这里插入图片描述
可以大概看到,不同的价格非常多,这里不截全图了。一共有3763种价格。但是大部分的价格都只出现一次。
1)下面通过作图看一下拟合一下价格的分布情况:

## 1) 总体分布概况
import scipy.stats as st
y = Train_data['price']
plt.figure(1); plt.title('Johnson SU')
sns.distplot(y, kde=False, fit=st.johnsonsu)  #无界约翰逊分布
plt.figure(2); plt.title('Normal')
sns.distplot(y, kde=False, fit=st.norm)  #正态分布分布
plt.figure(3); plt.title('Log Normal')
sns.distplot(y, kde=False, fit=st.lognorm)  #对数正态分布

补充:关于seaborn的学习可参考seaborn0.9中文文档。另外,关于scipy.stats的全部分布函数,可参考scipy.stats中的所有可用分布
这里我们看一下以上三种分布的拟合效果:
预测值分布拟合
价格不服从正态分布,所以在进行回归之前,它必须进行转换。虽然对数变换做得很好,但最佳拟合是无界约翰逊分布。

2)观察数据分布另外两个重要指标是峰度和偏度
峰度又称峰态系数,表征概率密度分布曲线在平均值处峰值高低的特征数,即是描述总体中所有取值分布形态陡缓程度的统计量。直观看来,峰度反映了峰部的尖度。这个统计量需要与正态分布相比较。

  • 峰度 =0表示该总体数据分布与正态分布的陡缓程度相同;
  • 峰度 >0表示该总体数据分布与正态分布相比较为陡峭,为尖顶峰;
  • 峰度 <0表示该总体数据分布与正态分布相比较为平坦,为平顶峰。

峰度的绝对值数值越大表示其分布形态的陡缓程度与正态分布的差异程度越大。

偏度描述的是某总体取值分布的对称性的特征统计量。

  • 偏度 =0表示其数据分布形态与正态分布的偏斜程度相同;
  • 偏度 >0表示其数据分布形态与正态分布相比为正偏(右偏),即有一条长尾巴拖在右边,数据右端有较多的极端值,数据均值右侧的离散程度强;
  • 偏度 <0表示其数据分布形态与正态分布相比为负偏(左偏),即有一条长尾拖在左边,数据左端有较多的极端值,数据均值左侧的离散程度强

偏度的绝对值数值越大表示其分布形态的偏斜程度越大。
下面通过代码看一下预测值价格的峰度和偏度:

# 查看skewness and kurtosis
sns.distplot(Train_data['price']);
print("Skewness: %f" % Train_data['price'].skew())  #偏度
print("Kurtosis: %f" % Train_data['price'].kurt())  #峰度

峰度和偏度
我们也可以看一下其他特征的分布情况:

Train_data.skew(), Train_data.kurt()

我们来可视化一下:

# 可视化所有特征的偏度
sns.distplot(Train_data.skew(),color='blue',axlabel ='Skewness')

所有特征偏度

# 可视化所有特征的峰度
sns.distplot(Train_data.kurt(),color='orange',axlabel ='Kurtness')

峰度
3)接下来我们可以看一下预测价格的频数分布

# 查看预测值的具体频数
plt.hist(Train_data['price'], orientation = 'vertical',histtype = 'bar', color ='red')
plt.show()

频数
查看频数, 大于20000得值极少,其实这里也可以把这些当作特殊得值(异常值)直接用填充或者删掉。
trick:这里车哥提供了一个trick。 log变换之后的分布较均匀,可以进行log变换进行预测。

# log变换 z之后的分布较均匀,可以进行log变换进行预测,这也是预测问题常用的trick
plt.hist(np.log(Train_data['price']), orientation = 'vertical',histtype = 'bar', color ='red') 
plt.show()

log变换

2.3.5 类别特征查看unique分布

特征分为类别特征和数字特征。
我们可以先分离掉预测值。

# 分离label即预测值
Y_train = Train_data['price']

这个区别方式适用于没有直接label coding的数据。这里不适用,需要人为根据实际含义来区分。一般的区分代码:

# numeric_features = Train_data.select_dtypes(include=[np.number])
# numeric_features.columns
# # 类型特征
# categorical_features = Train_data.select_dtypes(include=[np.object])
# categorical_features.columns

这里我们直接人为的定义好特征的类别。

numeric_features = ['power', 'kilometer', 'v_0', 'v_1', 'v_2', 'v_3', 'v_4', 'v_5', 'v_6', 'v_7', 'v_8', 'v_9', 'v_10', 'v_11', 'v_12', 'v_13','v_14' ]

categorical_features = ['name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'notRepairedDamage', 'regionCode',]

接着查看各个特征的分布:

# 特征nunique分布
for cat_fea in categorical_features:
    print(cat_fea + "的特征分布如下:")
    print("{}特征有个{}不同的值".format(cat_fea, Train_data[cat_fea].nunique()))
    print(Train_data[cat_fea].value_counts())

这里结果很长就不展示了。

2.3.6 数字特征分析

把预测价格也加上,算作数字特征

numeric_features.append('price')
numeric_features
# 结果如下
['power',
 'kilometer',
 'v_0',
 'v_1',
 'v_2',
 'v_3',
 'v_4',
 'v_5',
 'v_6',
 'v_7',
 'v_8',
 'v_9',
 'v_10',
 'v_11',
 'v_12',
 'v_13',
 'v_14',
 'price']

1)首先进行特征的相关性分析

# 1) 相关性分析
price_numeric = Train_data[numeric_features]
correlation = price_numeric.corr()
print(correlation['price'].sort_values(ascending = False),'\n')  # 只查看各个特征与预测值的相关性
# 结果如下
price        1.000000
v_12         0.692823
v_8          0.685798
v_0          0.628397
power        0.219834
v_5          0.164317
v_2          0.085322
v_6          0.068970
v_1          0.060914
v_14         0.035911
v_13        -0.013993
v_7         -0.053024
v_4         -0.147085
v_9         -0.206205
v_10        -0.246175
v_11        -0.275320
kilometer   -0.440519
v_3         -0.730946
Name: price, dtype: float64 

可以大概看到,v_8、v_12与预测值成较强的正相关性,v_3与预测值成较强的负相关。
下面通过热力图展示一下相关性:

f , ax = plt.subplots(figsize = (7, 7))
plt.title('Correlation of Numeric Features with Price',y=1,size=16)
sns.heatmap(correlation,square = True,  vmax=0.8)

相关性热力图
2)查看数字特征的峰度和偏值

del price_numeric['price']
## 2) 查看几个特征得 偏度和峰值
for col in numeric_features:
    print('{:15}'.format(col), 
          'Skewness: {:05.2f}'.format(Train_data[col].skew()) , 
          '   ' ,
          'Kurtosis: {:06.2f}'.format(Train_data[col].kurt())  
         )

结果如下:
特征的峰度和偏值】
3)查看每个数字特征的分布

## 3) 每个数字特征得分布可视化
f = pd.melt(Train_data, value_vars=numeric_features)
g = sns.FacetGrid(f, col="variable",  col_wrap=2, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")

特征的分布

可以看出匿名特征相对分布均匀。

4 ) 数字特征相互之间的关系可视化

sns.set()
columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5',  'v_2', 'v_6', 'v_1', 'v_14']
sns.pairplot(Train_data[columns],size = 2 ,kind ='scatter',diag_kind='kde')
plt.show()

pairplot主要展现的是变量两两之间的关系(线性或非线性,有无较为明显的相关关系)。关于sns.pairplot使用可参考sns.pairplot
查看结果如下:
特征相关关系图
看到对角线上是各个属性的直方图(分布图),而非对角线上是两个不同属性之间的相关图。举个例子,看倒数第二列,倒数第三行是v_6与v_1之间的关系,他俩的散点图基本呈现一个直线,有较明显的线性关系。

5 ) 多变量互相回归关系可视化
利用线性回归模型对数据进行拟合。

# 分别绘制这些数字特征与预测值的回归图
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6), (ax7, ax8), (ax9, ax10)) = plt.subplots(nrows=5, ncols=2, figsize=(24, 20))
# ['v_12', 'v_8' , 'v_0', 'power', 'v_5',  'v_2', 'v_6', 'v_1', 'v_14']
v_12_scatter_plot = pd.concat([Y_train,Train_data['v_12']],axis = 1)
sns.regplot(x='v_12',y = 'price', data = v_12_scatter_plot,scatter= True, fit_reg=True, ax=ax1)

v_8_scatter_plot = pd.concat([Y_train,Train_data['v_8']],axis = 1)
sns.regplot(x='v_8',y = 'price',data = v_8_scatter_plot,scatter= True, fit_reg=True, ax=ax2)

v_0_scatter_plot = pd.concat([Y_train,Train_data['v_0']],axis = 1)
sns.regplot(x='v_0',y = 'price',data = v_0_scatter_plot,scatter= True, fit_reg=True, ax=ax3)

power_scatter_plot = pd.concat([Y_train,Train_data['power']],axis = 1)
sns.regplot(x='power',y = 'price',data = power_scatter_plot,scatter= True, fit_reg=True, ax=ax4)

sns.regplot学习可参考:regplot绘制变量线性回归图
回归关系图

2.3.7 类别特征分析

先看一下我们有哪些类别特征:

categorical_features
# 结果如下
['name',
 'model',
 'brand',
 'bodyType',
 'fuelType',
 'gearbox',
 'notRepairedDamage',
 'regionCode']

1) unique分布
看一下各个类别特征分别有多少不同的种类:

## 1) unique分布
for fea in categorical_features:
    print(Train_data[fea].nunique())
# 结果如下
99662
248
40
8
7
2
2
7905

2) 类别特征箱形图可视化
箱形图(或盒须图)以一种利于变量之间比较或不同分类变量层次之间比较的方式来展示定量数据的分布。图中矩形框显示数据集的上下四分位数,而矩形框中延伸出的线段(触须)则用于显示其余数据的分布位置,剩下超过上下四分位间距的数据点则被视为“异常值”。
箱型图学习可参考:sns.boxplot绘制箱型图

# 因为 name和 regionCode的类别太稀疏了,这里我们把不稀疏的几类画一下
categorical_features = ['model',
 'brand',
 'bodyType',
 'fuelType',
 'gearbox',
 'notRepairedDamage']
for c in categorical_features:
    Train_data[c] = Train_data[c].astype('category')#  先把类别特征的特征值值转为类别类型
    if Train_data[c].isnull().any(): #如果该列存在缺失值
        Train_data[c] = Train_data[c].cat.add_categories(['MISSING']) # 就增加一个新类别'MISSING'
        Train_data[c] = Train_data[c].fillna('MISSING')  #缺失值填充为'MISSING'

def boxplot(x, y, **kwargs):
    sns.boxplot(x=x, y=y)   # 箱型图绘制接口
    x=plt.xticks(rotation=90)

f = pd.melt(Train_data, id_vars=['price'], value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable",  col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(boxplot, "value", "price")

箱型图

3)类别特征的小提琴图可视化
小提琴图结合了箱线图与核密度估计图的特点,中间就是一个盒须图,外面就套一层数据分布图。它表征了在一个或多个分类变量情况下,连续变量数据的分布并进行了比较,它是一种观察多个数据分布有效方法。

## 3) 类别特征的小提琴图可视化
catg_list = categorical_features
target = 'price'
for catg in catg_list :
    sns.violinplot(x=catg, y=target, data=Train_data)
    plt.show()
# 

小提琴图
4)类别特征的柱形图可视化
柱形图表示数值变量与每个矩形高度的中心趋势的估计值,并使用误差线提供关于该估计值附近的不确定性的一些指示。

## 4) 类别特征的柱形图可视化
def bar_plot(x, y, **kwargs):
    sns.barplot(x=x, y=y)
    x=plt.xticks(rotation=90)

f = pd.melt(Train_data, id_vars=['price'], value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable",  col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(bar_plot, "value", "price")

条形图
图中会对’value’列中的数值进行归类后计算price平均值,计算出来的值就作为条形图所显示的值(条形图上的误差棒则表示各类的数值相对于条形图所显示的值的误差)

5)类别特征的每个类别频数可视化(count_plot)
计算每个特征出现的频数:

##  5) 类别特征的每个类别频数可视化(count_plot)
def count_plot(x,  **kwargs):
    sns.countplot(x=x)
    x=plt.xticks(rotation=90)

f = pd.melt(Train_data,  value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable",  col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(count_plot, "value")

频数统计

2.3.8 用pandas_profiling生成数据报告

import pandas_profiling
pfr = pandas_profiling.ProfileReport(Train_data)
pfr.to_file("./example.html")

总结

数据探索在机器学习中我们一般称为EDA(Exploratory Data Analysis):是指对已有的数据(特别是调查或观察得来的原始数据)在尽量少的先验假定下进行探索,通过作图、制表、方程拟合、计算特征量等手段探索数据的结构和规律的一种数据分析方法。
数据探索有利于我们发现数据的一些特性,数据之间的关联性,对于后续的特征构建是很有帮助的。
1.对于数据的初步分析(直接查看数据,或.sum(), .mean(),.descirbe()等统计函数)可以从:样本数量,训练集数量,是否有时间特征,是否是时许问题,特征所表示的含义(非匿名特征),特征类型(字符类似,int,float,time),特征的缺失情况(注意缺失的在数据中的表现形式,有些是空的有些是”NAN”符号等),特征的均值方差情况。

2.分析记录某些特征值缺失占比30%以上样本的缺失处理,有助于后续的模型验证和调节,分析特征应该是填充(填充方式是什么,均值填充,0填充,众数填充等),还是舍去,还是先做样本分类用不同的特征模型去预测。

3.对于异常值做专门的分析,分析特征异常的label是否为异常值(或者偏离均值较远或者事特殊符号),异常值是否应该剔除,还是用正常值填充,是记录异常,还是机器本身异常等。

4.对于Label做专门的分析,分析标签的分布情况等。

5.进步分析可以通过对特征作图,特征和label联合做图(统计图,离散图),直观了解特征的分布情况,通过这一步也可以发现数据之中的一些异常值等,通过箱型图分析一些特征值的偏离情况,对于特征和特征联合作图,对于特征和label联合作图,分析其中的一些关联性。

通过这次的EDA实践,相信我们一定都会对数据探索的一般流程有了大概的了解。下面一节将是非常重要的特征工程,让我们敬请期待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值