文章目录

  • 官网文档
  • 环境
  • 泰坦尼克号数据分析
  • 泰坦尼克号数据清洗整理
  • Spark ML Pipeline
  • Titanic幸存者预测:逻辑回归LR模型
  • 模型训练
  • 模型预测
  • Titanic幸存者预测:决策树模型


官网文档

https://spark.apache.org/docs/2.4.5/api/python/pyspark.ml.html

环境

  • Python3
  • spark2.4.8
  • jupyternotebook
  • JDK8

jupyternotebook整合spark可参考:

泰坦尼克号数据分析

我们拿到数据肯定要先分析数据,然后才能进行特征工程。

通过图表、统计方法对数据的分布状态、字段数字特征,数值关系。描述数据分为趋势分析、离中趋势分析、相关分析

  • 趋势分析:平均数、中数、众数等
  • 离中趋势分析:最大值、最小值、四分差、平均差、方差、标准差
  • 相关分析:分析是否有统计学上的相关关系
import findspark
findspark.init()
##############################################
from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
spark = SparkSession.builder.master("local[*]").appName("PySpark ML").getOrCreate()
sc = spark.sparkContext
#############################################
df_train = spark.read.csv('./data/titanic-train.csv',header=True,inferSchema=True).cache()
#df_test = spark.read.csv('./data/titanic-test.csv',header=True,inferSchema=True).cache()
#计算基本的统计描述信息
print("离中趋势统计分析:数据维度描述 " + "===================================")
df_train.describe("Age","Pclass","SibSp","Parch").show()
df_train.describe("Sex","Cabin","Embarked","Fare","Survived").show()

print("相关分析:数值分析幸存者数量和性别关系 " + "===================================")
pdf = df_train.groupBy('sex', 'Survived').agg({'PassengerId':'count'}).withColumnRenamed('count(PassengerId)','count').orderBy('sex').toPandas()
print(pdf)

# 画图描述数据:幸存者和性别关系
print("画图描述数据:幸存者和性别关系 " + "===================================")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
## 防止中文乱码
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
width = 0.35
fig, ax = plt.subplots()
labels = ["female", 'male']
male_vs = pdf[pdf["Survived"] == 1]["count"]
female_vs = pdf[pdf["Survived"] == 0]["count"]
ax.bar(labels, male_vs, width, label='Survived')
ax.bar(labels, female_vs, width, bottom=male_vs, label='UnSurvived')
ax.set_ylabel('性别')
ax.set_title('Survived和性别关系分析')
ax.legend()
plt.show()

print("相关分析:数值分析幸存者数量和社会经济状态关系 " + "===================================")
pdf = df_train.groupBy('Pclass', 'Survived').agg({'PassengerId':'count'}).withColumnRenamed("count(PassengerId)", "count").toPandas()
print(pdf)

print("相关分析:数值分析幸存者数量和父母子女个数关系 " + "===================================")
pdf = df_train.groupBy('Parch', 'Survived').agg({'PassengerId':'count'}).withColumnRenamed("count(PassengerId)", "count").toPandas()
print(pdf)

print("相关分析:数值分析幸存者数量和兄弟姐妹及配偶的个数关系 " + "===================================")
pdf = df_train.groupBy('SibSp', 'Survived').agg({'PassengerId':'count'}).withColumnRenamed("count(PassengerId)", "count").toPandas()
print(pdf)

#############################################
sc.stop()

执行结果太长了不贴了,总之从上面经过数值分析,图示分析观察各个特征与幸存者是否存在关系,关系如何,进行主观的特征分析

泰坦尼克号数据清洗整理

经过数据观察分析知道了部分数据需要处理,比如进行格式转换、缺失值整理等特征工程处理

import findspark
findspark.init()
##############################################
from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
spark = SparkSession.builder \
        .master("local[*]") \
        .appName("PySpark ML") \
        .getOrCreate()
sc = spark.sparkContext
#############################################
df_train = spark.read.csv('./data/titanic-train.csv',header=True,inferSchema=True).cache()
#df_test = spark.read.csv('./data/titanic-test.csv',header=True,inferSchema=True).cache()

print("特征处理 " + "===================================")
#缺失值处理:年龄,用平均值填充到缺失值,计算得到平均年龄为29.699
df_train = df_train.fillna({'Age':round(29.699, 0)})
#缺失值处理:登陆港口,用登陆最多的港口'S'替换缺失值
df_train = df_train.fillna({'Embarked':'S'})
# 删除无用列
df_train = df_train.drop("Cabin")
df_train = df_train.drop("Ticket")
# 字符标签转为数值
from pyspark.ml.feature import StringIndexer, VectorAssembler, VectorIndexer
labelIndexer = StringIndexer(inputCol="Embarked", outputCol="iEmbarked")
model = labelIndexer.fit(df_train)
df_train = model.transform(df_train)
labelIndexer = StringIndexer(inputCol="Sex", outputCol="iSex")
model = labelIndexer.fit(df_train)
df_train = model.transform(df_train)
df_train.show(5)

# 特征选择
print("特征选择 " + "===================================")
features = ['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked','Survived']
train_features = df_train[features]
train_features.show(5)

# 特征向量化
print("特征向量化 " + "===================================")
df_assember = VectorAssembler(inputCols=['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked','Survived'], outputCol="features")
df = df_assember.transform(train_features)
df.show(5)

#############################################
# 测试数据做同样处理
df_test = spark.read.csv('./data/titanic-test.csv',header=True,inferSchema=True).cache()
#用平均值29.699替换缺失值
df_test = df_test.fillna({'Age': round(29.699,0)})
#用登录最多的港口'S'替换缺失值
df_test = df_test.fillna({'Embarked': 'S'})
# 删除无用列
df_test = df_test.drop("Cabin")
df_test = df_test.drop("Ticket")
# 字符标签转为数值
labelIndexer = StringIndexer(inputCol="Embarked", outputCol="iEmbarked")
model = labelIndexer.fit(df_test)
df_test = model.transform(df_test)
labelIndexer = StringIndexer(inputCol="Sex", outputCol="iSex")
model = labelIndexer.fit(df_test)
df_test = model.transform(df_test)
df_test.show()

# 特征选择
features = ['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked']
test_features = df_test[features]
test_features.show()

# 特征向量化
df_assembler = VectorAssembler(inputCols=['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked'], outputCol="features")
df2 = df_assembler.transform(test_features)

df2["features",].show()

#############################################
sc.stop()

执行结果也不完整贴了,很长,注释很清晰,贴前几个比较好看的

特征处理 ===================================
+-----------+--------+------+--------------------+------+----+-----+-----+-------+--------+---------+----+
|PassengerId|Survived|Pclass|                Name|   Sex| Age|SibSp|Parch|   Fare|Embarked|iEmbarked|iSex|
+-----------+--------+------+--------------------+------+----+-----+-----+-------+--------+---------+----+
|          1|       0|     3|Braund, Mr. Owen ...|  male|22.0|    1|    0|   7.25|       S|      0.0| 0.0|
|          2|       1|     1|Cumings, Mrs. Joh...|female|38.0|    1|    0|71.2833|       C|      1.0| 1.0|
|          3|       1|     3|Heikkinen, Miss. ...|female|26.0|    0|    0|  7.925|       S|      0.0| 1.0|
|          4|       1|     1|Futrelle, Mrs. Ja...|female|35.0|    1|    0|   53.1|       S|      0.0| 1.0|
|          5|       0|     3|Allen, Mr. Willia...|  male|35.0|    0|    0|   8.05|       S|      0.0| 0.0|
+-----------+--------+------+--------------------+------+----+-----+-----+-------+--------+---------+----+
only showing top 5 rows

特征选择 ===================================
+------+----+----+-----+-----+-------+---------+--------+
|Pclass|iSex| Age|SibSp|Parch|   Fare|iEmbarked|Survived|
+------+----+----+-----+-----+-------+---------+--------+
|     3| 0.0|22.0|    1|    0|   7.25|      0.0|       0|
|     1| 1.0|38.0|    1|    0|71.2833|      1.0|       1|
|     3| 1.0|26.0|    0|    0|  7.925|      0.0|       1|
|     1| 1.0|35.0|    1|    0|   53.1|      0.0|       1|
|     3| 0.0|35.0|    0|    0|   8.05|      0.0|       0|
+------+----+----+-----+-----+-------+---------+--------+
only showing top 5 rows

特征向量化 ===================================
+------+----+----+-----+-----+-------+---------+--------+--------------------+
|Pclass|iSex| Age|SibSp|Parch|   Fare|iEmbarked|Survived|            features|
+------+----+----+-----+-----+-------+---------+--------+--------------------+
|     3| 0.0|22.0|    1|    0|   7.25|      0.0|       0|(8,[0,2,3,5],[3.0...|
|     1| 1.0|38.0|    1|    0|71.2833|      1.0|       1|[1.0,1.0,38.0,1.0...|
|     3| 1.0|26.0|    0|    0|  7.925|      0.0|       1|[3.0,1.0,26.0,0.0...|
|     1| 1.0|35.0|    1|    0|   53.1|      0.0|       1|[1.0,1.0,35.0,1.0...|
|     3| 0.0|35.0|    0|    0|   8.05|      0.0|       0|(8,[0,2,5],[3.0,3...|
+------+----+----+-----+-----+-------+---------+--------+--------------------+
only showing top 5 rows

Spark ML Pipeline

机器学习由一系列步骤组成,Spark的Pipeline抽象定义了整个过程,我们只需按照特定顺序组装出一个Pipeline流水线即可

  • Transformer阶段调用transform()方法
  • Estimator阶段调用fit()方法来生成预测结果

如下Pipleline示例

import findspark
findspark.init()
##############################################
from pyspark.ml import Pipeline
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.feature import HashingTF, Tokenizer
from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local[*]").appName("PySpark ML").getOrCreate()
sc = spark.sparkContext
#############################################
#模拟训练数据,有PySpark的文本为1,其他为0
training = spark.createDataFrame([
    (0, "Hello PySpark", 1.0),
    (1, "Using Flink", 0.0),
    (2, "PySpark 3.0", 1.0),
    (3, "Test MySQL", 0.0)
], ["id", "text", "label"])
# pipeline 三个阶段: tokenizer(文本词向量化处理) -> hashingTF(文本特征向量化处理) -> logR.(LR模型)
tokenizer = Tokenizer(inputCol="text", outputCol="words")
hashingTF = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol="features")
logR = LogisticRegression(maxIter=10, regParam=0.001)
pipeline = Pipeline(stages=[tokenizer, hashingTF, logR])
#训练数据上进行pipeline fit操作,产生一个model
model = pipeline.fit(training)
#############################################
#测试集
test = spark.createDataFrame([
    (4, "PySpark Pipeline"),
    (5, "pipeline"),
    (6, "PySpark python"),
    (7, "julia c#")
], ["id", "text"])

#model执行transform
prediction = model.transform(test)
#预测
selected = prediction.select("id", "text", "probability", "prediction")
for row in selected.collect():
    tid, text, prob, prediction = row
    print("(%d, %s) --> prediction=%f,prob=%s" % (tid, text,  prediction,str(prob)))
#############################################
sc.stop()

执行结果

(4, PySpark Pipeline) --> prediction=1.000000,prob=[0.029796174862816768,0.9702038251371833]
(5, pipeline) --> prediction=0.000000,prob=[0.56611226449896,0.43388773550104]
(6, PySpark python) --> prediction=1.000000,prob=[0.029796174862816768,0.9702038251371833]
(7, julia c#) --> prediction=0.000000,prob=[0.56611226449896,0.43388773550104]

Titanic幸存者预测:逻辑回归LR模型

LR模型详细介绍可参考:

模型训练

import findspark
findspark.init()
##############################################
from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.ml.feature import StringIndexer, VectorAssembler
spark = SparkSession.builder.master("local[*]").appName("PySpark ML").getOrCreate()
sc = spark.sparkContext
#############################################
# 数据清洗、整理、特征选择、特征向量化
df_train = spark.read.csv('./data/titanic-train.csv',header=True,inferSchema=True) \
  .cache()
df_train = df_train.fillna({'Age': round(29.699,0)})
df_train = df_train.fillna({'Embarked': 'S'})
df_train = df_train.drop("Cabin")
df_train = df_train.drop("Ticket")
labelIndexer = StringIndexer(inputCol="Embarked", outputCol="iEmbarked")
model = labelIndexer.fit(df_train)
df_train = model.transform(df_train)
labelIndexer = StringIndexer(inputCol="Sex", outputCol="iSex")
model = labelIndexer.fit(df_train)
df_train = model.transform(df_train)
features = ['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked','Survived']
train_features = df_train[features]
df_assembler = VectorAssembler(inputCols=['Pclass', 'iSex', 'Age', 'SibSp',
   'Parch', 'Fare', 'iEmbarked'], outputCol="features")
train_data = df_assembler.transform(train_features)

# LR模型训练
from pyspark.ml.classification import LogisticRegression
lg = LogisticRegression(labelCol='Survived')
lgModel = lg.fit(train_data)
# 模型保存
lgModel.write().overwrite().save("./model/logistic-titanic")
print("save model to ./model/logistic-titanic")
# 查看模型
trainingSummary = lgModel.summary
trainingSummary.roc.show(5)
print("areaUnderROC: " + str(trainingSummary.areaUnderROC))

# 画图ROC
import matplotlib.pyplot as plt
plt.figure(figsize=(5,5))
plt.plot([0, 1], [0, 1], 'r--')
# pyspark bug,需要如下代码,否则直接collect会报错:AttributeError: 'NoneType' object has no attribute 'setCallSite'
df_FPR = lgModel.summary.roc.select('FPR')
df_FPR.sql_ctx.sparkSession._jsparkSession = spark._jsparkSession
df_FPR._sc = spark._sc
df_TPR = lgModel.summary.roc.select('TPR')
df_TPR.sql_ctx.sparkSession._jsparkSession = spark._jsparkSession
df_TPR._sc = spark._sc
plt.plot(df_FPR.collect(), df_TPR.collect())
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.show()

#############################################
sc.stop()

基于PyTorch搭建Informer模型实现时间序列预测 pyspark 时间序列预测_SparkML

注意,一般评判指标使用测试数据的结果去评判,包括AUC,ROC指标等等,上面为了方便先用训练数据画一下

模型预测

import findspark
findspark.init()
##############################################
from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.ml.feature import StringIndexer, VectorAssembler
spark = SparkSession.builder.master("local[*]").appName("PySpark ML").getOrCreate()
sc = spark.sparkContext
#############################################
df_test = spark.read.csv('./data/titanic-test.csv',header=True,inferSchema=True).cache()
# 数据清洗、整理、特征选择、特征向量化
df_test = df_test.fillna({'Age': round(29.699,0)})
df_test = df_test.fillna({'Embarked': 'S'})
df_test = df_test.fillna({'Fare': 36.0})
df_test = df_test.drop("Cabin")
df_test = df_test.drop("Ticket")
#新增Survived列,默认值为0
df_test = df_test.withColumn("Survived",0 * df_test["Age"])

labelIndexer = StringIndexer(inputCol="Embarked", outputCol="iEmbarked")
model = labelIndexer.fit(df_test)
df_test = model.transform(df_test)
labelIndexer = StringIndexer(inputCol="Sex", outputCol="iSex")
model = labelIndexer.fit(df_test)
df_test = model.transform(df_test)
features = ['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked','Survived']
test_features = df_test[features]
df_assembler = VectorAssembler(inputCols=['Pclass', 'iSex', 'Age', 'SibSp', 'Parch',
 'Fare', 'iEmbarked'], outputCol="features")
test = df_assembler.transform(test_features)

from pyspark.ml.classification import LogisticRegressionModel
lgModel = LogisticRegressionModel.load("./model/logistic-titanic")

testSummary =lgModel.evaluate(test)
results=testSummary.predictions
results["features","rawPrediction","probability","prediction"].show(5)
#############################################
sc.stop()

执行结果

+--------------------+--------------------+--------------------+----------+
|            features|       rawPrediction|         probability|prediction|
+--------------------+--------------------+--------------------+----------+
|[3.0,0.0,34.5,0.0...|[1.99328605097899...|[0.88009035220072...|       0.0|
|[3.0,1.0,47.0,1.0...|[0.63374031844971...|[0.65333708360849...|       0.0|
|[2.0,0.0,62.0,0.0...|[1.97058477648159...|[0.87767391006101...|       0.0|
|(7,[0,2,5],[3.0,2...|[2.21170839644084...|[0.90129601257823...|       0.0|
|[3.0,1.0,22.0,1.0...|[-0.2919725495559...|[0.42752102300610...|       1.0|
+--------------------+--------------------+--------------------+----------+
only showing top 5 rows

Titanic幸存者预测:决策树模型

决策树模型详细可参考:

import findspark
findspark.init()
##############################################
from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.ml.feature import StringIndexer, VectorAssembler
spark = SparkSession.builder.master("local[*]").appName("PySpark ML").getOrCreate()
sc = spark.sparkContext
#############################################
# 数据清洗、整理、特征选择、特征向量化
df_train = spark.read.csv('./data/titanic-train.csv',header=True,inferSchema=True) \
  .cache()
df_train = df_train.fillna({'Age': round(29.699,0)})
df_train = df_train.fillna({'Embarked': 'S'})
df_train = df_train.drop("Cabin")
df_train = df_train.drop("Ticket")
labelIndexer = StringIndexer(inputCol="Embarked", outputCol="iEmbarked")
model = labelIndexer.fit(df_train)
df_train = model.transform(df_train)
labelIndexer = StringIndexer(inputCol="Sex", outputCol="iSex")
model = labelIndexer.fit(df_train)
df_train = model.transform(df_train)
features = ['Pclass', 'iSex', 'Age', 'SibSp', 'Parch', 'Fare', 'iEmbarked','Survived']
train_features = df_train[features]
df_assembler = VectorAssembler(inputCols=['Pclass', 'iSex', 'Age', 'SibSp',
   'Parch', 'Fare', 'iEmbarked'], outputCol="features")
train_data = df_assembler.transform(train_features)

# 决策树模型训练
from pyspark.ml.classification import DecisionTreeClassifier
dtree = DecisionTreeClassifier(labelCol='Survived', featuresCol='features')
treeModel = dtree.fit(train_data)
print(treeModel.toDebugString)

# 对训练数据进行预测
dt_predictions = treeModel.transform(train_data)
dt_predictions.select("prediction", "Survived", "features").show()
from pyspark.ml.evaluation import MulticlassClassificationEvaluator
multi_evaluator = MulticlassClassificationEvaluator(labelCol = 'Survived', metricName = 'accuracy')
print('Decision Tree Accu:', multi_evaluator.evaluate(dt_predictions))
#############################################
sc.stop()

执行结果,其中因为开了决策树构建树模型过程debug打印,这里就不贴了,直接看预测结果

+----------+--------+--------------------+
|prediction|Survived|            features|
+----------+--------+--------------------+
|       0.0|       0|[3.0,0.0,22.0,1.0...|
|       1.0|       1|[1.0,1.0,38.0,1.0...|
|       1.0|       1|[3.0,1.0,26.0,0.0...|
|       1.0|       1|[1.0,1.0,35.0,1.0...|
|       0.0|       0|(7,[0,2,5],[3.0,3...|
|       0.0|       0|[3.0,0.0,30.0,0.0...|
|       0.0|       0|(7,[0,2,5],[1.0,5...|
|       0.0|       0|[3.0,0.0,2.0,3.0,...|
|       1.0|       1|[3.0,1.0,27.0,0.0...|
|       1.0|       1|[2.0,1.0,14.0,1.0...|
|       1.0|       1|[3.0,1.0,4.0,1.0,...|
|       1.0|       1|[1.0,1.0,58.0,0.0...|
|       0.0|       0|(7,[0,2,5],[3.0,2...|
|       0.0|       0|[3.0,0.0,39.0,1.0...|
|       1.0|       0|[3.0,1.0,14.0,0.0...|
|       1.0|       1|[2.0,1.0,55.0,0.0...|
|       0.0|       0|[3.0,0.0,2.0,4.0,...|
|       0.0|       1|(7,[0,2,5],[2.0,3...|
|       1.0|       0|[3.0,1.0,31.0,1.0...|
|       1.0|       1|[3.0,1.0,30.0,0.0...|
+----------+--------+--------------------+
only showing top 20 rows

Decision Tree Accu: 0.8417508417508418