kaggle入门项目python完整代码详解——泰坦尼克幸存者

实现思路

  1. 数据加载与合并:

    • 使用pandas读取训练集和测试集数据,然后合并,这便于对整体数据执行一致的预处理和分析。
  2. 数据探索与可视化:

    • 利用pandas进行基础数据探索(如检查缺失值、数据类型、基本统计描述)。
    • 使用matplotlibseaborn进行数据可视化,探索不同特征与目标变量(生存率)之间的关系。
  3. 特征工程:

    • 基于探索分析的结果,进行特征选择和转换,例如转换类别数据、处理缺失值、创建新的交互特征等。
  4. 模型构建与评估:

    • 选择多种机器学习模型进行训练,比如随机森林、支持向量机、逻辑回归等。
    • 对模型进行训练并评估其性能,使用如交叉验证等方法进行模型选择。
  5. 结果预测与提交:

    • 对测试数据集使用最优模型进行预测。
    • 将预测结果保存并准备提交格式代码

代码解释

import warnings
warnings.filterwarnings("ignore") #忽略警告信息

# 数据处理清洗包
import pandas as pd
import numpy as np
import random as rnd

# 可视化包
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

# 机器学习算法相关包
from sklearn.linear_model import LogisticRegression, Perceptron, SGDClassifier
from sklearn.svm import SVC, LinearSVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

SVC()
  • %matplotlib inline: 是一个魔法命令,它可以在IPython或Jupyter Notebook中内嵌绘图,并且可以省略掉plt.show()
  • Scikit-learn (sklearn): 是Python中使用最广泛的机器学习库之一,包含了从数据预处理到训练模型的各种工具。
    • LogisticRegressionPerceptronSGDClassifier: 线性模型,用于分类任务。
    • SVCLinearSVC: 支持向量机,用于分类和回归分析。
    • KNeighborsClassifier: 最近邻算法,用于分类和回归。
    • GaussianNB: 朴素贝叶斯分类器,适用于大量特征的数据。
    • DecisionTreeClassifier: 决策树分类器,用于分类和回归。
    • RandomForestClassifier: 随机森林是一个包含多个决策树的分类器,用于分类和回归,能有效处理过拟合。
  • SVC() 是创建一个支持向量机分类器的实例,通常用于比较不同模型
train_df = pd.read_csv('./titanic/train.csv')
test_df = pd.read_csv('./titanic/test.csv')
combine = [train_df, test_df] # 合并数据

合并数据便于对整体数据执行一致的预处理和分析。

# 获取所有特征名
print(train_df.columns.values)

# 预览前五个数据
train_df.head()

# 预览后五个数据
train_df.tail()
print(train_df.isnull().sum()) #找出不同特征的缺失值数量
print('_'*40) #分割线
test_df.isnull().sum() #最后一个单元格表达式自动显示

test_df.isnull().sum() #在Jupyter Notebook中,单元格中的最后一个表达式的结果会被自动展示,这使得在一些情况下可以不必显式地使用print()函数。这种方式可以让代码看起来更简洁,尤其是在进行探索性数据分析时,可以快速查看数据而不需要过多的print()调用

train_df.info()
print('_'*40)
test_df.info()

info()方法详细列出了DataFrame中的每列,包括其名称、非空值的数量以及每列的数据类型。此外,还提供了DataFrame使用的内存量。

  • 在交互式环境中:如Jupyter Notebook,info()的输出自动显示,不需要print()
  • 在非交互式环境中(如直接在Python脚本中运行):info()的输出仍然会显示在控制台,因为它设计为直接输出到标凈输出流
round(train_df.describe(percentiles=[.5, .6, .7, .75, .8, .9, .99]),2)
  1. train_df.describe():

    • describe() 是一个生成描述性统计数据的方法,常用于快速查看数据的分布情况。
    • 默认情况下,它提供 count(计数)、mean(均值)、std(标准差)、min(最小值)、25%(第一四分位数)、50%(中位数)、75%(第三四分位数)和 max(最大值)。
  2. percentiles=[.5, .6, .7, .75, .8, .9, .99]:

    • 通过 percentiles 参数,可以指定需要计算的具体百分位数。
    • 在这个例子中,除了常规的 25%、50%、75% 外,还额外计算了 60%、70%、80%、90% 和 99% 的百分位数。
    • 50%(0.5)其实就是中位数,这里显示了将其作为百分位数的一部分也是可以的。
  3. round(..., 2):

    • round() 函数用于对 describe() 方法的结果进行四舍五入。
    • 这里的 , 2 表示将结果四舍五入到小数点后两位。
    • 这通常用于改进数据的呈现,使其更易读,尤其是在处理包含很多小数的数据时。

运行结果

列的解释:

  1. PassengerId:乘客的ID号。
  2. Survived:乘客是否存活,其中1表示存活,0表示未存活。
  3. Pclass:乘客的舱位等级,一个分类变量,代表1等舱、2等舱和3等舱。
  4. Age:乘客的年龄。
  5. SibSp:乘客在船上的兄弟姐妹或配偶的数量。
  6. Parch:乘客在船上的父母或子女的数量。
  7. Fare:乘客为船票支付的费用。

统计量的解释:

  • count:表示每个特征列的非空值数量。例如,Age列有714个非空值,说明有部分乘客的年龄数据缺失。
  • mean:平均值。例如,Survived的平均值为0.38,表明大约38%的乘客存活。
  • std:标准差,表示数据的离散程度。例如,Age的标准差为14.53,说明乘客年龄分布的波动较大。
  • min:最小值。例如,Fare的最小值为0.00,表明有乘客没有为船票支付费用。
  • 50% (中位数):数据的中间点。例如,Pclass的中位数为3,表明大多数乘客处于3等舱。
  • 60%, 70%, 75%, 80%, 90%, 99%:这些都是不同的百分位数,提供了关于数据分布的更多细节。例如,99%的乘客年龄小于或等于65.87岁。
  • max:最大值。例如,Age的最大值为80岁,表明船上最老的乘客是80岁。

分析例子:

  • Survived

    • 平均值为0.38,说明大约38%的乘客存活。
    • 50% (中位数) 为0,说明超过一半的乘客未存活。
    • 99%的百分位数为1,说明除了极少数之外,绝大多数乘客都未能存活。
  • Fare

    • 平均值为32.20,但中位数仅为14.45,表明船票费用分布极不均匀,少数高票价拉高了平均值。
    • 最高票价为512.33,远高于大多数乘客的票价,表明可能有极少数的高级舱位。

describe函数的使用

describe() 是 pandas 库中的一个强大函数,用于生成关于 DataFrame 或 Series 对象中数据的描述性统计摘要。这通常包括均值、标准差、最小值、最大值以及各个百分位数。

describe() 函数可以通过几个参数来定制其行为:

percentiles:一个介于0和1之间的数值列表,用于指定计算哪些百分位数。默认是 [0.25, 0.5, 0.75],分别对应25%,50%(中位数),75% 百分位数。

df.describe(percentiles=[0.2, 0.4, 0.6, 0.8])

include:用于指定应该包括哪些数据类型的列在输出中。默认情况下,只描述数值型列。可以设置为 'all' 包括所有列,或者一个数据类型列表。

df.describe(include='all')  # 描述所有列,包括非数值型
df.describe(include=[np.number])  # 仅描述数值型列
df.describe(include=[np.object])  # 仅描述对象类型列

exclude:与 include 相反,用于指定排除哪些数据类型的列。

df.describe(exclude=[np.number])  # 排除所有数值型列
train_df.describe(include=['O'])
  • 使用 include=['O'] 会导致 describe() 方法生成那些类型为对象的列的统计摘要,这包括:
    • count:非空(非 NaN)值的数量。
    • unique:不重复值的数量,展示该列中有多少个唯一的值。
    • top:出现频率最高的值。
    • freq:出现频率最高的值的频率(即该值出现了多少次)。

对部分特征与survived的相关性进行分析,初步判断有用特征量

# 针对Pclass和Survived进行分类汇总
train_df[['Pclass','Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
# pd.DataFrame(train_df.groupby('Pclass', as_index=False)['Survived'].mean()).sort_values(by='Survived', ascending=False)

train_df[['Sex','Survived']].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)

train_df[['SibSp','Survived']].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived',ascending=False)

train_df[['Parch','Survived']].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived',ascending=False)
g = sns.FacetGrid(train_df, col='Survived')  #FacetGrid(data, row, col, hue, height, aspect, palette, ...)
g.map(plt.hist, 'Age', bins=20)
  • sns.FacetGridseaborn 库中用于创建一个网格,可以在这个网格中绘制多个图表,每个图表显示数据的一个子集。
  • train_df 是包含数据的 DataFrame。
  • col='Survived' 参数指定网格的列(col)基于 Survived 列的不同值来分割。因为 Survived 是一个二元变量(通常取值 0 或 1),所以这将生成两个面板(facet):一个为存活(Survived=1),一个为未存活(Survived=0)。
  • g.map() 函数用于在每个分面上应用一个绘图函数。
  • plt.histmatplotlib.pyplot 的函数,用于绘制直方图。
  • 'Age' 指定了直方图的数据列,这里是乘客的年龄。
  • bins=20 指定将数据分成20个等宽的条形,这有助于更细致地观察年龄分布的形状。
#grid = sns.FacetGrid(train_df, col='Survived', row='Pclass', size=2.5, aspect=1.6)
grid = sns.FacetGrid(train_df, col='Pclass', hue='Survived') 
#alpha是透明度
grid.map(plt.hist, 'Age', alpha=0.5, bins=20)
grid.add_legend();
grid = sns.FacetGrid(train_df, col='Embarked')

grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')

#add_legend():这是 seaborn FacetGrid 对象的方法,用于在图形中添加图例。图例显示的是图形中代表的#不同类别(如果 FacetGrid 在创建时使用了 hue 参数指定了颜色区分的变量)。
#如果 FacetGrid 被定义时包含了 hue 参数(这个参数通常用于按某个变量的不同值对数据进行颜色编码),#那么 add_legend() 就会在图表中添加一个图例,标明不同颜色代表的数据类别。

grid.add_legend();
grid = sns.FacetGrid(train_df, col='Embarked', hue='Survived', palette={0: 'b', 1: 'r'})
grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
grid.add_legend()

输出

整理清洗数据

移除不需要的列

  • 在代码中,使用 combine[0].shapecombine[1].shape 来显示数据处理前后这两个 DataFrame 的形状,这有助于确认是否正确地执行了如删除列等操作。
print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)

train_df = train_df.drop(['Ticket', 'Cabin'], axis=1)
test_df = test_df.drop(['Ticket', 'Cabin'], axis=1)
combine = [train_df, test_df]

"After", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape

提取新的特征(将乘客的称呼作为了一个新特征)

# 使用正则表达式提取Title特征
for dataset in combine:
    dataset['Title'] = dataset.Name.str.extract('([A-Za-z]+)\.', expand=False)

pd.crosstab(train_df['Title'], train_df['Sex']).sort_values(by='female', ascending=False)  # pd.crosstab列联表
  • 循环遍历 combine 列表combine 是一个包含两个 DataFrame (train_dftest_df) 的列表。这个循环确保对列表中的每个 DataFrame 执行相同的操作。

  • 使用 str.extract() 方法:这是 pandas 中的一个字符串方法,用来对字符串列应用正则表达式,并提取匹配的部分。这里的正则表达式 ([A-Za-z]+)\. 匹配任何连续的字母字符(至少一个),后跟一个点(.)。这种模式匹配常见于诸如 "Mr.", "Mrs.", "Dr." 等称谓。

  • expand=False:此参数设置为 False 表示返回的结果是一个 Series,而不是 DataFrame。

  • pd.crosstab:这是 pandas 中的一个函数,用于计算两个(或更多)因子的简单交叉表。它非常适合于快速汇总信息,并查看两个变量之间的关系。

  • 参数:这里 train_df['Title'] 作为行索引(index),train_df['Sex'] 作为列索引(columns)。这将创建一个展示不同称谓和性别组合的频率分布表。

  • sort_values(by='female', ascending=False):这个方法用于对结果进行排序,基于 'female' 列的值降序排序。这样做可以快速查看哪些称谓最常与女性关联。

grid = sns.FacetGrid(train_df,  col='Title', hue='Survived', col_wrap=3, size=2.5, aspect=1.6)
#grid = sns.FacetGrid(train_df, col='Title', hue='Survived') 
grid.map(plt.hist, 'Age', alpha=0.5, bins=20)
grid.add_legend()
# 可以用更常见的名称替换许多标题或将它们归类为稀有
for dataset in combine:
    dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',
                                                 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')

    dataset['Title'] = dataset['Title'].replace(['Mlle', 'Ms'], 'Miss')
    dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
    
train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
# 将分类标题转换为序数
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}
for dataset in combine:
    dataset['Title'] = dataset['Title'].map(title_mapping)  # 将序列中的每一个元素,输入函数,最后将映射后的每个值返回合并,得到一个迭代器
    dataset['Title'] = dataset['Title'].fillna(0)  

train_df.head()
# 转换分类特征Sex
for dataset in combine:
    dataset['Sex'] = dataset['Sex'].map( {'female': 1, 'male': 0} ).astype(int)  #男性赋值为0,女性赋值为1,并转换为整型数据

train_df.head()
# 绘制Age, Pclass, Sex复合直方图
#grid = sns.FacetGrid(train_df, row='Pclass', col='Sex', size=2.2, aspect=1.6)
grid = sns.FacetGrid(train_df, col='Pclass', hue='Sex')
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend()
# 创建空数组
guess_ages = np.zeros((2,3))
guess_ages

# 遍历 Sex (0 或 1) 和 Pclass (1, 2, 3) 来计算六种组合的 Age 猜测值
for dataset in combine:
    # 第一个for循环计算每一个分组的Age预测值
    for i in range(0, 2):
        for j in range(0, 3):
            guess_df = dataset[(dataset['Sex'] == i) & \
                                  (dataset['Pclass'] == j+1)]['Age'].dropna()

            # age_mean = guess_df.mean()
            # age_std = guess_df.std()
            # age_guess = rnd.uniform(age_mean - age_std, age_mean + age_std)

            age_guess = guess_df.median()

            # 将随机年龄浮点数转换为最接近的 0.5 年龄(四舍五入)
            guess_ages[i,j] = int( age_guess/0.5 + 0.5 ) * 0.5
            
    # 第二个for循环对空值进行赋值        
    for i in range(0, 2):
        for j in range(0, 3):
            dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),\
                    'Age'] = guess_ages[i,j]

    dataset['Age'] = dataset['Age'].astype(int)
train_df.head()
# 创建年龄段,并确定其与Survived的相关性
# 一般在建立分类模型时,需要对连续变量离散化,特征离散化后,模型会更稳定,降低了模型过拟合的风险
train_df['AgeBand'] = pd.cut(train_df['Age'], 5)  # 将年龄分割为5段,等距分箱
train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)

# 将这些年龄区间替换为序数
for dataset in combine:    
    dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
    dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
    dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
    dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
    dataset.loc[ dataset['Age'] > 64, 'Age'] = 4
train_df.head()

train_df = train_df.drop(['AgeBand'], axis=1)  # 删除训练集中的AgeBand特征
combine = [train_df, test_df]
train_df.head()
test_df

for dataset in combine:
    dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1

train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)

# 创建新特征IsAlone
for dataset in combine:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1

train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()

train_df = train_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
test_df = test_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
combine = [train_df, test_df]

train_df.head()

# 创建Age*Pclass特征以此用来结合Age和Pclass变量
for dataset in combine:
    dataset['Age*Pclass'] = dataset.Age * dataset.Pclass

train_df.loc[:, ['Age*Pclass', 'Age', 'Pclass']].head(10)
train_df[['Age*Pclass', 'Survived']].groupby(['Age*Pclass'], as_index=False).mean()

freq_port = train_df.Embarked.dropna().mode()[0] 
for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
    
train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)

# 同样转换分类特征为序数
for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)

train_df.head()


# 测试集中Fare有一个缺失值,用中位数进行填补
test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
test_df.head()

plt.hist(train_df['Fare'])

train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)  # 根据样本分位数进行分箱,等频分箱
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)

for dataset in combine:
    dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
    dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
    dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare']   = 2
    dataset.loc[ dataset['Fare'] > 31, 'Fare'] = 3
    dataset['Fare'] = dataset['Fare'].astype(int)

train_df = train_df.drop(['FareBand'], axis=1)
combine = [train_df, test_df]
    
train_df.head(10)
test_df.head(10)

构建模型

X_train = train_df.drop("Survived", axis=1)
Y_train = train_df["Survived"]
X_test  = test_df.drop("PassengerId", axis=1).copy()
X_train.shape, Y_train.shape, X_test.shape

# 逻辑回归模型
logreg = LogisticRegression()
logreg.fit(X_train, Y_train)
Y_pred = logreg.predict(X_test)  # logreg.predict_proba(X_test)[:,1]
acc_log = round(logreg.score(X_train, Y_train) * 100, 2)
acc_log


coeff_df = pd.DataFrame(train_df.columns.delete(0))
coeff_df.columns = ['Feature']
coeff_df["Correlation"] = pd.Series(logreg.coef_[0])
coeff_df.sort_values(by='Correlation', ascending=False)

# 支持向量机模型
svc = SVC()
svc.fit(X_train, Y_train)
Y_pred = svc.predict(X_test)
acc_svc = round(svc.score(X_train, Y_train) * 100, 2)
acc_svc

# KNN
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X_train, Y_train)
Y_pred = knn.predict(X_test)
acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
acc_knn

# 朴素贝叶斯分类器
gaussian = GaussianNB()
gaussian.fit(X_train, Y_train)
Y_pred = gaussian.predict(X_test)
acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
acc_gaussian

# 感知机
perceptron = Perceptron()
perceptron.fit(X_train, Y_train)
Y_pred = perceptron.predict(X_test)
acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
acc_perceptron

# 线性SVC
linear_svc = LinearSVC()
linear_svc.fit(X_train, Y_train)
Y_pred = linear_svc.predict(X_test)
acc_linear_svc = round(linear_svc.score(X_train, Y_train) * 100, 2)
acc_linear_svc

# SDG随机梯度下降
sgd = SGDClassifier()
sgd.fit(X_train, Y_train)
Y_pred = sgd.predict(X_test)
acc_sgd = round(sgd.score(X_train, Y_train) * 100, 2)
acc_sgd

# 决策树
decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, Y_train)
Y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)
acc_decision_tree

# 决策树可视化
from sklearn import tree
import graphviz
import pydotplus  
from IPython.display import Image
dot_data = tree.export_graphviz(decision_tree, out_file=None, 
                         feature_names=X_train.columns,  
                         class_names=['0','1'],  
                         max_depth=3,
                         filled=True, rounded=True,  
                         special_characters=True)  
graph = pydotplus.graph_from_dot_data(dot_data)  
Image(graph.create_png())

dot_data = tree.export_graphviz(decision_tree, out_file=None,
                         feature_names=X_train.columns,  
                         #class_names=['0','1'],  
                         filled=True, rounded=True,  
                         special_characters=True)  
graph = pydotplus.graph_from_dot_data(dot_data)  
graph.write_pdf("DTtree.pdf") 

train_df[(train_df['Age*Pclass']>2.5) & (train_df['Fare']<=1.5)&(train_df['Fare']>0.5)&(train_df['IsAlone']>0.5)&(train_df['Age']>0.5)& \
         (train_df['Embarked']<=1.5) &(train_df['Embarked']>0.5)&(train_df['Fare']<=2.5)&(train_df['Age']<=1.5)&(train_df['Pclass']>1.5)& \
         (train_df['Title']<=1.5)]

# 随机森林
random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)
acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
acc_random_forest

 模型评估和提交结果

models = pd.DataFrame({
    'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression', 
              'Random Forest', 'Naive Bayes', 'Perceptron', 
              'Stochastic Gradient Decent', 'Linear SVC', 
              'Decision Tree'],
    'Score': [acc_svc, acc_knn, acc_log, 
              acc_random_forest, acc_gaussian, acc_perceptron, 
              acc_sgd, acc_linear_svc, acc_decision_tree]})
models.sort_values(by='Score', ascending=False)
submission = pd.DataFrame({
        "PassengerId": test_df["PassengerId"],
        "Survived": Y_pred
    })
submission.to_csv('./submission.csv', index=False)

 

  • 49
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值