python------数据预处理与特征工程(总)

python------数据预处理与特征工程(总)

​ 相关数据集均放置在本人的csdn文件中,如有需要,可免费下载。内容来自老师上课的课件。侵删…

一、数据挖掘的五大流程

  • 获取数据
  • 数据预处理 :数据预处理是从数据中检测,纠正或删除损坏,不准确或不适用于模型的记录的过程。可能面对的问题有:数据类型不同,比如有的是文字,有的是数字,有的含时间序列,有的连续,有的间断。也可能,数据的质量不行,有噪声,有异常,有缺失,数据出错,量纲不一,有重复,数据是偏态,数据量太大或太小。数据预处理的目的:让数据适应模型,匹配模型的需求
  • 特征工程: 特征工程是将原始数据转换为更能代表预测模型的潜在问题的特征的过程,可以通过挑选最相关的特征,提取特征以及创造特征来实现。其中创造特征又经常以降维算法的方式实现。可能面对的问题有:特征之间有相关性,特征和标签无关,特征太多或太小,或者干脆就无法表现出应有的数据现象或无法展示数据的真实面貌
    特征工程的目的:降低计算成本,提升模型上限。
  • 建模,测试模型并预测出结果
  • 上线,验证模型效果

二、数据的无量纲化

​ 在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布的需求,这种需求统称为将数据“无量纲化”。譬如梯度和矩阵为核心的算法中,譬如逻辑回归,支持向量机,神经网络,无量纲化可以加快求解速度;而在距离类模型,譬如K近邻,K-Means聚类中,无量纲化可以帮我们提升模型精度,避免某一个取值范围特别大的特征对距离计算造成影响。(一个特例是决策树和树的集成算法们,对决策树我们不需要无量纲化,决策树可以把任意数据都处理得很好。)
​ 数据的无量纲化可以是线性的,也可以是非线性的。线性的无量纲化包括中心化(Zero-centered或者Meansubtraction)处理和缩放处理(Scale)。中心化的本质是让所有记录减去一个固定值,即让数据样本数据平移到某个位置。缩放的本质是通过除以一个固定值,将数据固定在某个范围之中,取对数也算是一种缩放处理。

三、归一化与标准化

MinMaxScaler

​ 当数据**(x)按照最小值中心化后,再按极差(最大值 - 最小值)缩放,数据移动了最小值个单位,并且会被收敛到[0,1]之间,而这个过程,就叫做数据归一化(Normalization),又称(Min-Max Scaling)**。注意,Normalization是归一化,不是正则化,真正的正则化是regularization,不是数据预处理的一种手段。归一化之后的数据服从正态分布。 MinMaxScaler有一个重要参数,feature_range,控制我们希望把数据压缩到的范围,默认是[0,1]。

在这里插入图片描述

StandardScaler

​ 当数据**(x)按均值(μ)中心化后,再按标准差(σ)缩放,数据就会服从为均值为0**,方差为1的正态分布(即标准正态分布),而这个过程,就叫做数据标准化(Standardization,又称Z-score normalization)
在这里插入图片描述

归一化与标准化的对比

​ 大多数机器学习算法中,会选择StandardScaler来进行特征缩放,因为MinMaxScaler对异常值非常敏感。在PCA,聚类,逻辑回归,支持向量机,神经网络这些算法中,StandardScaler往往是最好的选择。MinMaxScaler在不涉及距离度量、梯度、协方差计算以及数据需要被压缩到特定区间时使用广泛,比如数字图像处理中量化像素强度时,都会使用MinMaxScaler将数据压缩于[0,1]区间之中。建议先试试看StandardScaler,效果不好换MinMaxScaler

实例代码

  1. 导入库并准备好以下数据

    from sklearn.preprocessing import MinMaxScaler 
    data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
    
  2. 对以上数据使用数据归一化

    import pandas as pd
    pd.DataFrame(data)
    #实现归一化
    scaler = MinMaxScaler()                      #实例化
    scaler = scaler.fit(data)                    #fit,在这里本质是生成min(x)和max(x)
    result = scaler.transform(data)              #通过接口导出结果
    result
    
  3. 将归一化后的结果逆转:使用inverse_transform

    result_ = scaler.fit_transform(data)
    scaler.inverse_transform(result)
    
  4. ​ 使用MinMaxScaler的参数feature_range实现将数据归一化到[0,1]以外的范围中【5,10】

    data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
    scaler = MinMaxScaler(feature_range=[5,10])
    result = scaler.fit_transform(data)                 
    result
    
  5. 对以上数据进行标准化处理。

    from sklearn.preprocessing import StandardScaler
    data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
    scaler = StandardScaler()                           #实例化
    scaler.fit(data)
    
  6. 查看查看均值的属性mean_

    scaler.mean_   
    scaler.var_
    x_std = scaler.transform(data)
    x_std.mean()
    
  7. 查看方差的属性var

    x_std.std() 
    
  8. 使用inverse_transform逆转标准化

scaler.fit_transform(data)   
scaler.inverse_transform(x_std)  

四、缺失值的处理

背景

​ 机器学习和数据挖掘中所使用的数据,永远不可能是完美的。很多特征,对于分析和建模来说意义非凡,但对于实际收集数据的人却不是如此,因此数据挖掘之中,常常会有重要的字段缺失值很多,但又不能舍弃字段的情况。因此,数据预处理中非常重要的一项就是处理缺失值。在这里,我们使用从泰坦尼克号提取出来的数据,这个数据有三个特征,一个数值型,两个字符型,标签也是字符型。在python的机器学习中,我们常用的包是sklearn。

常用参数

from sklearn.impute import SimpleImputer

其常用参数有:

  • missing_values:告诉SimpleImputer,数据中的缺失值长什么样,默认空值np.nan
  • strategy: 填补缺失值的策略,默认均值。
    1. 输入“mean”使用均值填补(仅对数值型特征可用)
    2. 输入“median"用中值填补(仅对数值型特征可用)
    3. 输入"most_frequent”用众数填补(对数值型和字符型特征都可用)
    4. 输入“constant"表示请参考参数“fill_value"中的值(对数值型和字符型特征都可用)
  • fill_value:当参数startegy为”constant"的时候可用,可输入字符串或数字表示要填充的值,常用0
  • copy: 默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原本的特征矩阵中去。

实例代码

  1. 读取泰坦尼克缩减版数据并探索

    import pandas as pd
    data = pd.read_csv(r".\Narrativedata.csv" ,index_col=0)
    #index_col=0将第0列作为索引,不写则认为第0列为特征 
    data.head()
    data.info()
    
  2. 对年龄数据进行各种方式的填补

    #填补年龄
    Age = data.loc[:,"Age"].values.reshape(-1,1)        #sklearn当中特征矩阵必须是二维
    Age[:20]
    from sklearn.impute import SimpleImputer
    imp_mean = SimpleImputer()                      #实例化,默认均值填补
    imp_median = SimpleImputer(strategy="median")  #用中位数填补
    imp_0 = SimpleImputer(strategy="constant",fill_value=0) 	#用0填补
    imp_mean = imp_mean.fit_transform(Age)              
    imp_median = imp_median.fit_transform(Age)
    imp_0 = imp_0.fit_transform(Age)
    imp_mean[:20]
    imp_median[:20]
    imp_0[:20]
    data.loc[:,"Age"] = imp_median 							#最终选择中位数
    data.info()
    
  3. 使用众数填补Embarked

    Embarked = data.loc[:,"Embarked"].values.reshape(-1,1)
    imp_mode = SimpleImputer(strategy = "most_frequent")
    data.loc[:,"Embarked"] = imp_mode.fit_transform(Embarked)
    data.info()
    

五、编码和哑变量

背景

​ 在机器学习中,大多数算法,譬如逻辑回归,支持向量机SVM,k近邻算法等都只能够处理数值型数据,不能处理文字,在sklearn当中,除了专用来处理文字的算法,其他算法在fit的时候全部要求输入数组或矩阵,也不能够导入文字型数据(其实手写决策树和普斯贝叶斯可以处理文字,但是sklearn中规定必须导入数值型)。

​ 然而在现实中,许多标签和特征在数据收集完毕的时候,都不是以数字来表现的。比如说,学历的取值可以是[“小学”,“初中”,“高中”,“大学”],付费方式可能包含[“支付宝”,“现金”,“微信”]等等。在这种情况下,为了让数据适应算法和库,我们必须将数据进行编码,即是说,将文字型数据转换为数值型。

分类

preprocessing.LabelEncoder:标签专用,能够将分类转换为分类数值

preprocessing.OrdinalEncoder:特征专用,能够将分类特征转换为分类数值

preprocessing.OneHotEncoder:独热编码,创建哑变量

注:以上均来自于sklearn包

三中类型的区别

  • 舱门(S,C,Q)

三种取值S,C,Q是相互独立的,彼此之间完全没有联系,表达的是S≠C≠Q的概念。这是名义变量。

  • 学历(小学,初中,高中)

三种取值不是完全独立的,我们可以明显看出,在性质上可以有高中>初中>小学这样的联系,学历有高低,但是学历取值之间却不是可以计算的,我们不能说小学 + 某个取值 = 初中。这是有序变量。

  • 体重(>45kg,>90kg,>135kg)

各个取值之间有联系,且是可以互相计算的,比如120kg - 45kg = 90kg,分类之间可以通过数学计算互相转换。这是有距变量。

​ 然而在对特征进行编码的时候,这三种分类数据都会被我们转换为[0,1,2],这三个数字在算法看来,是连续且可以计算的,这三个数字相互不等,有大小,并且有着可以相加相乘的联系。所以算法会把舱门,学历这样的分类特征,都误会成是体重这样的分类特征。这是说,我们把分类转换成数字的时候,忽略了数字中自带的数学性质,所以给算法传达了一些不准确的信息,而这会影响我们的建模。类别OrdinalEncoder可以用来处理有序变量,但对于名义变量,我们只有使用哑变量的方式来处理,才能够尽量向算法传达最准确的信息

简单地说,LabelEncoder即用于将True/False转换为数值类型,如0/1。OrdinalEncoder用于处理有序变量。OneHotEncoder用于处理名义变量(即哑变量,又叫做虚拟变量)。

Binarizer与KBinsDiscretizer

  • sklearn.preprocessing.Binarizer根据阈值将数据二值化(将特征值设置为0或1),用于处理连续型变量。大于阈值的值映射为1,而小于或等于阈值的值映射为0。默认阈值为0时,特征中所有的正值都映射到1。二值化是对文本计数数据的常见操作,分析人员可以决定仅考虑某种现象的存在与否。比如把年龄进行二值化处理:大于18岁和小于等于18岁。
  • preprocessing.KBinsDiscretizer可以将连续型变量划分为多个分类变量的类,能够将连续型变量排序后按顺序分箱后编码。比如对年龄进行独热等宽分箱(“uniform”:表示等宽分箱,即每个特征中的每个箱的最大值之间的差为(特征.max() - 特征.min())/(n_bins))“quantile”:表示等位分箱,即每个特征中的每个箱内的样本数量都相同;“kmeans”:表示按聚类分箱,每个箱中的值到最近的一维k均值聚类的簇心得距离都相同。

示例代码

  1. 读取泰坦尼克简化数据
import pandas as pd
data_ = pd.read_csv(r".\Narrativedata.csv"
                  ,index_col=0
                 )#index_col=0将第0列作为索引,不写则认为第0列为特征
data_.head()
data_.info()
  1. 年龄用中位数填充

    data_.loc[:,"Age"] = data_.loc[:,"Age"].fillna(data_.loc[:,"Age"].median())
    
  2. Embark空值行直接删除

    data_.dropna(axis=0,inplace=True)
    data_.info()
    
  3. preprocessing.LabelEncoder:标签专用,能够将分类转换为分类数值:把最后一列Survived转换为分类数值。

from sklearn.preprocessing import LabelEncoder
y = data_.iloc[:,-1]              #要输入的是标签,不是特征矩阵,所以允许一维
le = LabelEncoder()             #实例化
le = le.fit(y)                     #导入数据
label = le.transform(y)           #transform接口调取结果
le.classes_                       #属性.classes_查看标签中究竟有多少类别
  1. preprocessing.OrdinalEncoder:特征专用,能够将分类特征转换为分类数值:把Sex和Embarked转换为分类数值
from sklearn.preprocessing import OrdinalEncoder
data_.head()
OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories_
data_.iloc[:,1:-1] = OrdinalEncoder().fit_transform(data_.iloc[:,1:-1])
data_.head()
  1. preprocessing.OneHotEncoder:独热编码,创建哑变量:把Sex和Embarked生成独热编码,并修改列名
from sklearn.preprocessing import OneHotEncoder
data = pd.read_csv(r".\Narrativedata.csv"
                   ,index_col=0
                  ) #index_col=0将第0列作为索引,不写则认为第0列为特征
data.loc[:,"Age"] = data_.loc[:,"Age"].fillna(data_.loc[:,"Age"].median())
data.dropna(axis=0,inplace=True) #处理缺失值
X = data.iloc[:,1:-1]
X.info()
enc = OneHotEncoder(categories='auto').fit(X)
result = enc.transform(X).toarray()
result
enc.get_feature_names() #返回每一个经过哑变量后生成稀疏矩阵列的名字
#axis=1,表示跨行进行合并,也就是将两表左右相连,如果是axis=0,就是将量表上下相连
newdata = pd.concat([data,pd.DataFrame(result)],axis=1)
newdata.head()
newdata.drop(["Sex","Embarked"],axis=1,inplace=True) #把多余的特征删除
 
newdata.columns = ["Age","Survived","Female","Male","Embarked_C",
"Embarked_Q","Embarked_S"] #对特征列重新命名
newdata.head()
  1. 把年龄二值化,以30岁作为阈值

    data_2 = data.copy()
    data_2.info()
    from sklearn.preprocessing import Binarizer
    X = data_2.iloc[:,0].values.reshape(-1,1)       #类为特征专用,所以不能使用一维数组
    transformer = Binarizer(threshold=30).fit_transform(X)
    data_2.iloc[:,0] = transformer
    data_2.head()
    

六、特征选择—手写数字

基本概念

特征提取 (feature extraction):从文字,图像,声音等其他非结构化数据中提取新信息作为特征。比如说,从淘宝宝贝的名称中提取出产品类别,产品颜色,是否是网红产品等等。

特征创造 (feature creation):把现有特征进行组合,或互相计算,得到新的特征。比如说,我们有一列特征是速度,一列特征是距离,我们就可以通过让两列相处,创造新的特征:通过距离所花的时间。

特征选择 (feature selection):从所有的特征中,选择出有意义,对模型有帮助的特征,以避免必须将所有特征都导入模型去训练的情况。

我们有四种方法可以用来选择特征:过滤法,嵌入法,包装法,和降维算法

降维算法会在后面单独介绍,这里暂且不提。

选择特征的方法:

过滤法:

Filter过滤法:过滤方法通常用作预处理步骤,特征选择完全独立于任何机器学习算法。它是根据各种统计检验中的分数以及相关性的各项指标来选择特征。
在这里插入图片描述

方差过滤(importmant)

采用方差过滤VarianceThreshold的方法进行特征选择:设定一个让特征总数减半的方差阈值,只要找到特征方差的中位数,再将这个中位数作为参数threshold的值输入。并用交叉验证随机森林来进行评分。这是通过特征本身的方差来筛选特征的类。比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用。所以无论接下来的特征工程要做什么,都要优先消除方差为0的特征。VarianceThreshold有重要参数threshold,表示方差的阈值,表示舍弃所有方差小于threshold的特征,不填默认为0,即删除所有的记录都相同的特征。

相关性过滤之卡方过滤(impormant)

卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。卡方检验类feature_selection.chi2计算每个非负特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名。再结合feature_selection.SelectKBest这个可以输入**”评分标准来选出前K**个分数最高的特征的类,我们可以借此除去最可能独立于标签,与我们分类目的无关的特征。

相关性过滤之互信息法(了解一下)

互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。互信息法不返回p值或F值类似的统计量,它返回“每个特征与目标之间的互信息量的估计”,这个估计量在[0,1]之间取值,为0则表示两个变量独立,为1则表示两个变量完全相关。使用互信息法选取数据特征。

嵌入法

嵌入法是一种让算法自己决定使用哪些特征的方法,即特征选择和算法训练同时进行。在使用嵌入法时,我们先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小选择特征。这些权值系数往往代表了特征对于模型的某种贡献或某种重要性,比如决策树和树的集成模型中的feature_importances_属性,可以列出各个特征对树的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征。另外,嵌入法引入了算法来挑选特征,因此其计算速度也会和应用的算法有很大的关系。如果采用计算量很大,计算缓慢的算法,嵌入法本身也会非常耗时耗力。并且,在选择完毕之后,我们还是需要自己来评估模型。

在这里插入图片描述
在这里插入图片描述

estimator**:**使用的模型评估器,只要是带feature_importances_或者coef_属性,或带有l1和l2惩罚项的模型都可以使用

threshold: 特征重要性的阈值,重要性低于这个阈值的特征都将被删除

prefit: 默认False,判断是否将实例化后的模型直接传递给构造函数。如果为True,则必须直接调用fit和transform,不能使用fit_transform,并且SelectFromModel不能与cross_val_score,GridSearchCV和克隆估计器的类似实用程序一起使用。

norm_order: k可输入非零整数,正无穷,负无穷,默认值为1。在评估器的coef_属性高于一维的情况下,用于过滤低于阈值的系数的向量的范数的阶数

max_features**:**在阈值设定下,要选择的最大特征数。要禁用阈值并仅根据max_features选择,请设置threshold = -np.inf

包装法(比嵌入法算的算的更慢,比较细致,性能很强,但效率不高)

包装法也是一个特征选择和算法训练同时进行的方法,与嵌入法十分相似,它也是依赖于算法自身的选择,比如coef_属性或feature_importances_属性来完成特征选择。从当前的一组特征中修剪最不重要的特征。在修剪的集合上递归地重复该过程,直到最终到达所需数量的要选择的特征。区别于过滤法和嵌入法的一次训练解决所有问题,包装法要使用特征子集进行多次训练,因此它所需要的计算成本是最高的。

最典型的目标函数是递归特征消除法(Recursive feature elimination, 简写为RFE)。它是一种贪婪的优化算法,旨在找到性能最佳的特征子集。 它反复创建模型,并在每次迭代时保留最佳特征或剔除最差特征,下一次迭代时,它会使用上一次建模中没有被选中的特征来构建下一个模型,直到所有特征都耗尽为止。 然后,它根据自己保留或剔除特征的顺序来对特征进行排名,最终选出一个最佳子集。包装法的效果是所有特征选择方法中最利于提升模型表现的,它可以使用很少的特征达到很优秀的效果。除此之外,在特征数目相同时,包装法和嵌入法的效果能够匹敌,不过它比嵌入法算得更见缓慢,所以也不适用于太大型的数据。相比之下,包装法是最能保证模型效果的特征选择方法。

在这里插入图片描述
在这里插入图片描述

参数estimator是需要填写的实例化后的评估器,n_features_to_select是想要选择的特征个数,step表示每次迭代中希望移除的特征个数。除此之外,RFE类有两个很重要的属性,.support_:返回所有的特征的是否最后被选中的布尔矩阵,以及.ranking_返回特征的按数次迭代中综合重要性的排名。

示例代码:

  1. 读取手写数字文件并区分出特征和标签

    import pandas as pd
    data = pd.read_csv(r".\digit recognizor.csv")
    X = data.iloc[:,1:]
    y = data.iloc[:,0]
    X.shape
    data.head()
    
  2. 读取手写数字文件并区分出特征和标签

from sklearn.feature_selection import VarianceThreshold
selector = VarianceThreshold()              #实例化,不填参数默认方差为0
X_var0 = selector.fit_transform(X)            #获取删除不合格特征之后的新特征矩阵
X_var0.shape#(42000, 708)
pd.DataFrame(X_var0).head()
  1. 使用方差过滤保留一半的特征,并用随机森林交叉验证获得平均分(cv=5)

    import numpy as np
    # X.var()#每一列的方差
    X_fsvar = VarianceThreshold(np.median(X.var().values)).fit_transform(X)
    X_fsvar.shape
    cross_val_score(RFC(n_estimators=10,random_state=0),X_fsvar,y,cv=5).mean()
    
  2. 用卡方过滤选取300个特征。并用交叉验证随机森林来进行评分

    from sklearn.ensemble import RandomForestClassifier as RFC
    from sklearn.model_selection import cross_val_score
    from sklearn.feature_selection import SelectKBest
    from sklearn.feature_selection import chi2
    X_fschi = SelectKBest(chi2, k=300).fit_transform(X_fsvar, y)
    X_fschi.shape
    cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
    
    可以看出,模型的效果降低了,这说明我们在设定k=300的时候删除了与模型相关且有效的特征,我们的K值设置得太小,要么我们需要调整K值,要么我们必须放弃相关性过滤。当然,如果模型的表现提升,则说明我们的相关性过滤是有效的,是过滤掉了模型的噪音的,这时候我们就保留相关性过滤的结果。
    
  3. 通过学习曲线来找到卡方过滤中的最佳特征数量取值(200-400,步长为10)

    score = []
    for i in range(390,200,-10):
        X_fschi = SelectKBest(chi2, k=i).fit_transform(X_fsvar, y)
    once = cross_val_score(RFC(n_estimators=10,random_state=0),
    X_fschi,y,cv=5).mean()
        score.append(once)
    plt.plot(range(390,200,-10),score)
    plt.show()
    
  4. 使用互信息法检测特征的互信息量

from sklearn.feature_selection import mutual_info_classif as MIC
result = MIC(X_fsvar,y)#互信息法
k = result.shape[0] - sum(result <= 0)
#所有特征的互信息量估计都大于0,因此所有特征都与标签相关
  1. 使用嵌入法选出阈值为0.005的特征

    from sklearn.feature_selection import SelectFromModel
    from sklearn.ensemble import RandomForestClassifier as RFC 
    RFC_ = RFC(n_estimators =10,random_state=0)
    X_embedded = SelectFromModel(RFC_,threshold=0.005).fit_transform(X,y)
    X_embedded.shape
    
  2. 使用细化学习曲线找到嵌入法最佳阈值,并做交叉验证评分

    score2 = []
    for i in np.linspace(0, 0.00134,20):
        X_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(X,y)
        once = cross_val_score(RFC_,X_embedded,y,cv=5).mean()
        score2.append(once)
    plt.figure(figsize=[20,5])
    plt.plot(np.linspace(0,0.00134,20),score2)
    plt.xticks(np.linspace(0,0.00134,20))
    plt.show()
    
  3. 使用包装法选出340个特征并用随机森林评分;使用学习曲线找到包装法最佳特征取值(1,750),步长为50

    from sklearn.feature_selection import RFE
    RFC_ = RFC(n_estimators =10,random_state=0)
    selector = RFE(RFC_, n_features_to_select=340, step=50).fit(X, y)
    selector.support_.sum()#340
    selector.ranking_
    X_wrapper = selector.transform(X) 
    cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
    
    #======【TIME WARNING: 15 mins】======#
    score = []
    for i in range(1,751,50):
        X_wrapper = RFE(RFC_,n_features_to_select=i, step=50).fit_transform(X,y)
        once = cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
        score.append(once)
    plt.figure(figsize=[20,5])
    plt.plot(range(1,751,50),score)
    plt.xticks(range(1,751,50))
    plt.show()
    
    由于性能原因,所以处理结果及其漫长
    

未完待续。。。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值