Spark机器学习--运用逻辑回归分析银行营销数据

数据分析与数据处理实例(某银行数据) 掌握numpy、matplotlib、pandas模块为前提,kmeans算法为主要分析工具 数据分析与数据处理 数据及含义 这里先上截图,具体的数据及数据含义点击下边链接,自行获取! https://download.youkuaiyun.com/download/weixin_44423698/11737958 方法 探索数据、清洗数据、清洗过后的选择需要的数据、对数据进行转换(如果数据很清晰,而且... 阅读详情

Spark机器学习--运用逻辑回归分析银行营销数据

一、介绍

1.1 内容

存款营销是银行吸收存款的主要经营模式,通过现有数据建立模型来判断客户是否订阅存款业务,从而帮助商业银行更好的分配人力资源,提高业务量,以满足现阶段营销活动对提高营销成功率的期望。

本实验会使用spark机器学习中的逻辑回归算法,分析银行营销数据,按照机器学习开发步骤,建立逻辑回归模型,预测客户是否会存款,并评估预测模型的精确度。

实验将采用如下步骤进行回归机器学习算法开发:

æ­¤å¤è¾å¥å¾ççæè¿°

 

1.2 知识点

  • Spark机器学习开发的常规步骤
  • Spark框架提供的特征转换算法--StringIndex
  • Spark框架提供的特征转换算法--OneHotEncoder
  • Spark提供的API对预测结果准确度进行评估
  • Spark SQL在Spark机器学习中的用法

1.3 环境

  • 编程语言为Scala
  • 编程软件为实验环境中提供的Scala IDE工具
  • Spark API版本为2.1

二、理论学习

2.1 逻辑回归(Logistic Regression)

机器学习算法分为监督学习算法和无监督学习算法,在监督学习算法中,算法有目标变量(即预测值)和特征变量等两种变量,根据目标变量的值是离散值还是连续数值,分为分类算法(目标变量的值为离散值,如 是/否、0/1)和回归算法(目标变量为连续值)。

逻辑回归是一个分类算法而不是回归算法。通常是利用已知的特征变量来预测一个离散型目标变量的值(如0/1,是/否,真/假)。通过拟合一个逻辑函数来预测一个事件发生的概率,预测值是一个概率值(0-100%),根据概率值的大小,映射为目标变量的分类值,如:概率值大于等于 50%,映射目标变量分类值为 1,概率值小于 50%,映射目标变量分类值为 0。

2.2 逻辑回归与线性回归

为了更好理解逻辑回归算法,可以与线性回归算法做个对比。线性回归对多维空间中存在的样本点,用特征的线性组合去拟合多维空间中点的分布和轨迹。线性回归的公式如下: 此处输入图片的描述 对于逻辑回归来说,其思想也是基于线性回归。其公式如下: 此处输入图片的描述 其中, 此处输入图片的描述 被称作sigmoid函数,Logistic Regression算法将线性函数的结果映射到了sigmoid函数中。sigmoid的函数图形如下:

hθ(x)<0.5 则说明当前样本数据属于A类; 
hθ(x)>0.5 则说明当前样本数据属于B类。

所以可以将sigmoid函数看成样本数据的概率函数。Spark提供的逻辑回归算法,对样本数据的预测结果中,有一列存储了概率值。本实验中将此概率值进行了输出。

2.3 特征工程

特征工程是最大限度地从原始数据中提取特征以供算法和模型使用的过程。特征工程包括特征提取、特征转换、降维等操作。 Spark提供了多种特征工程算法,详细内容可查看官方文档。

本实验中,我们使用 StringIndex 和 OneHotEncoder 两种特征转换算法。

2.2.1 StringIndex

StringIndexer 是指把一组字符型标签编码成一组数值型标签索引,索引的范围为 0 到标签数量,索引构建的顺序为标签的频率,优先编码频率较大的标签,所以出现频率最高的标签为 0 号。如果输入的是数值型的,会转成字符型,再对其进行编码。

Spark官方实例: 下列数据,包括id和category两列:

 id | category
----|----------
 0  | a
 1  | b
 2  | c
 3  | a
 4  | a
 5  | c

使用StringIndexer特征转换算法,对上述数据category列进行特征转换,设置生成新列名为categoryIndex,结果为:

 id | category | categoryIndex
----|----------|---------------
 0  | a        | 0.0
 1  | b        | 2.0
 2  | c        | 1.0
 3  | a        | 0.0
 4  | a        | 0.0
 5  | c        | 1.0

上述结果中,由于 a 出现了 3 次,c 出现了 2 次,b 出现了 1 次,按照出现频率由高到低,从 0 开始编码,所以 a 的编码为 0.0,c 的编码为1.0,b 的编码为2.0。

2.2.2 OneHotEncoder

OneHot编码将已经转换为数值型的类别特征,映射为一个稀疏向量对象,对于某一个类别映射的向量中只有一位有效,即只有一位数字是 1,其他数字位都是 0。如下面的例子,有如下两个特征属性:

  • 婚姻状况:["已婚","单身","离异","未知"]
  • 有无房贷:["有房贷","无房贷"]

对于某一个样本,如["已婚","无房贷"],因为机器学习算法不接收字符型的特征值,我们需要将这个分类值的特征数字化,最直接的方法,可以采用序列化的方式:[0,1]。但是这样的特征处理并不能直接放入机器学习算法中。对于这个问题,婚姻状况是4维的,有无房贷是2维的,这样,我们可以采用One-Hot编码的方式对上述的样本["已婚","无房贷"]编码,"已婚"对应[1,0,0,0],"无房贷"对应[0,1],则完整的特征数字化的结果为:[1,0,0,0,0,1],这样做结果就是数据会变得连续,但也会非常的稀疏,所以在Spark中,使用了稀疏向量来表示这个结果。

逻辑回归算法的分类器需要连续数值作为特征输入。

三、步骤

3.1 获取银行营销数据

通过命令,可获取本次实验数据。

wget http://labfile.oss.aliyuncs.com/courses/990/bank_marketing_data.zip

该数据来自于https://github.com/ChitturiPadma/datasets/blob/master/bank_marketing_data.csv

在本实验中所用到的实验数据都存储在/opt/train/bank_marketing_data.csv,注意下载数据后,请拷贝到对应的路径下。下面的代码以此作为依据。

3.2 分析营销数据结构

3.2.1 数据结构描述

在数据bank_marketing_data.csv中,包含4万多条记录和21个字段,本次实验中,我们使用其中10个字段作为因变量,1个字段作为目标变量,进行分析预测。11个字段的名称和中文含义如下:

字段名称中文含义
age客户年龄
job客户职业
marital婚姻状况
default是否有信用违约
housing是否有住房贷款
loan是否有住房贷款
duration最后一次联系持续时间(秒)
previous之前活动中与用户联系次数
poutcome之前市场营销活动的结果
empvarrate就业变化速率
y目标变量,本次活动实施结果:是否同意存款

3.2.2 编写程序查看数据结构

  1. 打开开发环境:ScalaIDE,创建一个Scala项目:sparkml,创建package:com.shiyanlou.algorithm用来存放机器学习算法相关的程序。创建Scala Object:LogisiticRegressionTest。
  2. 将/opt/spark-2.1.0-bin-hadoop2.6/jars/目录下的所有jar包引入到项目类路径。
  3. 在LogisiticRegressionTest Object中创建main方法,此时的项目结构如下: 

æ­¤å¤è¾å¥å¾ççæè¿°

 

  1. 在main方法中编写如下代码:
    //实例化SparkSession对象
    val spark=SparkSession.builder().appName("Logistic_Prediction").master("local").getOrCreate()
    //设置日志级别,减少日志输出,便于查看运行结果
    spark.sparkContext.setLogLevel("WARN")
    //导入隐式转换包,方便使用转换函数
    import spark.implicits._
    //读取数据,传入数据路径/opt/train/bank_marketing_data.csv
    val bank_Marketing_Data=spark.read
                                 .option("header", true)
                                 .option("inferSchema", "true")
                                 .csv("/opt/train/bank_marketing_data.csv")
    //查看营销数据的前5条记录,包括所有字段
    println("all columns data:")
    bank_Marketing_Data.show(5)
    //读取营销数据指定的11个字段,并将age、duration、previous三个字段的类型从Integer类型转换为Double类型
    val selected_Data=bank_Marketing_Data.select("age", 
                                                "job",
                                                "marital", 
                                                "default", 
                                                "housing", 
                                                "loan", 
                                                "duration",
                                                "previous", 
                                                "poutcome", 
                                                "empvarrate", 
                                                "y")
                                                .withColumn("age", bank_Marketing_Data("age").cast(DoubleType))
                                                .withColumn("duration", bank_Marketing_Data("duration").cast(DoubleType))
                                                .withColumn("previous", bank_Marketing_Data("previous").cast(DoubleType))
     //显示前5条记录,只包含指定的11个字段
     println("11 columns data:")
     selected_Data.show(5)  
     //显示营销数据的数据量
     println("data count:"+selected_Data.count())
    
  2. 上述代码,首先实例化SparkSession对象,接着读取营销数据bank_marketing_data.csv,将其中age,duration,previous三列由Integer转换成Double类型。查看数据信息,对要分析的数据有一个大概了解。需要注意的是:我们只分析数据中选中的11列,选中的数据存储在了selected_Data变量中。

运行程序,查看运行结果: 

æ­¤å¤è¾å¥å¾ççæè¿°

 

运行结果包括三部分:包含所有列的前5条记录、包含指定11列的前5条记录和数据总条数:41188条。

上述操作可执行完整代码如下:

package com.shiyanlou.algorithm

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.DoubleType

object LogisiticRegressionTest {
  def main(args: Array[String]): Unit = {
    val spark=SparkSession.builder().appName("Logistic_Prediction").master("local").getOrCreate()
    spark.sparkContext.setLogLevel("WARN")
    import spark.implicits._
    val bank_Marketing_Data=spark.read
                                 .option("header", true)
                                 .option("inferSchema", "true")
                                 .csv("/opt/train/bank_marketing_data.csv")
    println("all columns data:")
    bank_Marketing_Data.show(5)
    val selected_Data=bank_Marketing_Data.select("age", 
                                                "job",
                                                "marital", 
                                                "default", 
                                                "housing", 
                                                "loan", 
                                                "duration",
                                                "previous", 
                                                "poutcome", 
                                                "empvarrate", 
                                                "y")
                                                .withColumn("age", bank_Marketing_Data("age").cast(DoubleType))
                                                .withColumn("duration", bank_Marketing_Data("duration").cast(DoubleType))
                                                .withColumn("previous", bank_Marketing_Data("previous").cast(DoubleType))
     println("11 columns data:")
     selected_Data.show(5)  

     println("data count:"+selected_Data.count())

  }
}

3.3 概要分析数据字段内容

机器学习算法接收的数据都是向量对象,向量对象中的元素值都是数值型的。由于我们所分析的数据中存在非数值型的字段列,我们需要对其进行分析,尤其是包含分类值的字段列,需要统计出分类值和分类数量,为下一步的特征转换做准备。

3.3.1 在main方法中继续编写如下代码:

     //对数据进行概要统计
     val summary=selected_Data.describe()
     println("Summary Statistics:")
     //显示概要统计信息
     summary.show()

     //查看每一列所包含的不同值数量
     val columnNames=selected_Data.columns
     val uniqueValues_PerField=columnNames.map { field => field+":"+selected_Data.select(field).distinct().count() }
     println("Unique Values For each Field:")
     uniqueValues_PerField.foreach(println)

3.3.2 运行程序,查看运行结果。上述程序主要输出内容如下:

概要统计信息

æ­¤å¤è¾å¥å¾ççæè¿°

 

统计信息输出,包括12列: 第1列为统计值,总数、平均值、方差值、最小值、最大值; 第2-12列为指定的数据字段值。从中可以看出每一列的总数、平均值、方差值、最小值、最大值是多少。如果某列内容不是数值,则某些统计信息会是null。

每一列包含多少个不同值

æ­¤å¤è¾å¥å¾ççæè¿°

 

从输出可以看出,age列有78个不同的值,job列有12个不同的值,后续的输出可以以此类推... 需要注意:job、marital、default、housing、poutcome、loan这6列的值是分类值,如 marital列:婚姻状况,有已婚、单身、离异、未知等分类值。

截至到此可执行的完整代码如下:

package com.shiyanlou.algorithm

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.DoubleType

object LogisiticRegressionTest {
  def main(args: Array[String]): Unit = {
    val spark=SparkSession.builder().appName("Logistic_Prediction").master("local").getOrCreate()
    spark.sparkContext.setLogLevel("WARN")
    import spark.implicits._
    val bank_Marketing_Data=spark.read
                                 .option("header", true)
                                 .option("inferSchema", "true")
                                 .csv("/opt/train/bank_marketing_data.csv")
    println("all columns data:")
    bank_Marketing_Data.show(5)
    val selected_Data=bank_Marketing_Data.select("age", 
                                                "job",
                                                "marital", 
                                                "default", 
                                                "housing", 
                                                "loan", 
                                                "duration",
                                                "previous", 
                                                "poutcome", 
                                                "empvarrate", 
                                                "y")
                                                .withColumn("age", bank_Marketing_Data("age").cast(DoubleType))
                                                .withColumn("duration", bank_Marketing_Data("duration").cast(DoubleType))
                                                .withColumn("previous", bank_Marketing_Data("previous").cast(DoubleType))
     println("11 columns data:")
     selected_Data.show(5)  

     println("data count:"+selected_Data.count())

     val summary=selected_Data.describe()
     println("Summary Statistics:")
     summary.show()

     val columnNames=selected_Data.columns
     val uniqueValues_PerField=columnNames.map { field => field+":"+selected_Data.select(field).distinct().count() }
     println("Unique Values For each Field:")
     uniqueValues_PerField.foreach(println)
  }
}

3.4 特征工程

经过对数据的概要分析,我们知道,营销数据中除了数值型的字段(age、duration、previous、empvarrate),还有一些包含分类值的字符型字段(job、marital、default、housing、poutcome、loan)。本步骤就要利用特征工程,对分类字段进行特征转换,使用spark提供的StringIndex和OneHotEncoder算法。

3.4.1 为了方便测试,我们在项目中新建一个Scala Object:LogisiticRegressionTest2,新建main方法。

3.4.2 在main方法中编写如下代码:

//实例化SparkSession对象
val spark=SparkSession.builder().appName("Logistic_Prediction").master("local").getOrCreate()
//设置日志级别,减少日志输出,便于查看运行结果
spark.sparkContext.setLogLevel("WARN")
//导入隐式转换包,方便使用转换函数
import spark.implicits._
//读取数据,传入数据路径/opt/train/bank_marketing_data.csv
val bank_Marketing_Data=spark.read
                             .option("header", true)
                             .option("inferSchema", "true")   .csv("/opt/train/bank_marketing_data.csv")
//查看营销数据的前5条记录,包括所有字段
bank_Marketing_Data.show(5)
//读取营销数据指定的11个字段,并将age、duration、previous三个字段的类型从Integer类型转换为Double类型
val selected_Data=bank_Marketing_Data.select("age", 
                                            "job",
                                            "marital", 
                                            "default", 
                                            "housing", 
                                            "loan", 
                                            "duration",
                                            "previous", 
                                            "poutcome", 
                                            "empvarrate", 
                                            "y")
                                            .withColumn("age", bank_Marketing_Data("age").cast(DoubleType))
                                            .withColumn("duration", bank_Marketing_Data("duration").cast(DoubleType))
                                            .withColumn("previous", bank_Marketing_Data("previous").cast(DoubleType))

val indexer = new StringIndexer().setInputCol("job").setOutputCol("jobIndex")
val indexed = indexer.fit(selected_Data).transform(selected_Data)
indexed.printSchema()
indexed.show
val encoder = new OneHotEncoder().setDropLast(false).setInputCol("jobIndex").setOutputCol("jobVec")
val encoded = encoder.transform(indexed) 
encoded.show()
encoded.printSchema()

首先获取需要分析的数据字段,存入selected_Data变量,此步骤与上一个类LogisiticRegressionTest中的代码相同。 接着将对分类字段 job 使用 StringIndex 数据转换算法,将分类值转换为数值类型,输入列为 job,输出列为 jobIndex,为 OneHot 编码做准备。 然后对 jobIndex 字段使用 OneHotEncoder 数据转换算法,输入列为 StringIndex 数据转换算法的输出列,输出列为 jobVec。

 

输出结果如下:

 æ­¤å¤è¾å¥å¾ççæè¿°

 

输出结果中的内容分为两部分:数据内容和数据结构。

从上方的数据内容可以看出,对 job 列应用数据转换算法(StringIndexer和OneHotEncoder),输出了新列:jobIndex 和 jobVec。 从下方的数据内容可以看出,jobIndex列为double类型,jobVec列为向量类型。

StringIndex 数据转换算法为数据增加了一列,列名为jobIndex,列类型为double,原始列job 并没有删除。由于原始列值不是数值型,无法参与向量运算,所以可以将对应的原始列删除;

OneHotEncoder 数据转换算法为数据增加了一列,列名为 jobVec,列类型为 Vector,原始列 jobIndex 并没有删除。

LogisiticRegressionTest2 类的完整代码如下:

package com.shiyanlou.algorithm

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.DoubleType
import org.apache.spark.ml.feature.StringIndexer
import org.apache.spark.ml.feature.OneHotEncoder

object LogisticRegressionTest2 {
   def main(args: Array[String]): Unit = {
    val spark=SparkSession.builder().appName("Logistic_Prediction").master("local").getOrCreate()
    spark.sparkContext.setLogLevel("WARN")
    import spark.implicits._
    val bank_Marketing_Data=spark.read
                                 .option("header", true)
                                 .option("inferSchema", "true")   
                                 .csv("/opt/train/bank_marketing_data.csv")
    bank_Marketing_Data.show(5)
    val selected_Data=bank_Marketing_Data.select("age", 
                                                "job",
                                                "marital", 
                                                "default", 
                                                "housing", 
                                                "loan", 
                                                "duration",
                                                "previous", 
                                                "poutcome", 
                                                "empvarrate", 
                                                "y")
                                                .withColumn("age", bank_Marketing_Data("age").cast(DoubleType))
                                                .withColumn("duration", bank_Marketing_Data("duration").cast(DoubleType))
                                                .withColumn("previous", bank_Marketing_Data("previous").cast(DoubleType))

    val indexer = new StringIndexer().setInputCol("job").setOutputCol("jobIndex")
    val indexed = indexer.fit(selected_Data).transform(selected_Data)
    indexed.printSchema()
    indexed.show
    val encoder = new OneHotEncoder().setDropLast(false).setInputCol("jobIndex").setOutputCol("jobVec")
    val encoded = encoder.transform(indexed) 
    encoded.show()
    encoded.printSchema()
   }
}

3.4.3 同理,对marital、default、housing、poutcome、loan 等5个分类列进行 OneHot 编码,在 main 方法中已有代码后面,继续编写代码:

val indexer = new StringIndexer().setInputCol("job").setOutputCol("jobIndex")
val indexed = indexer.fit(selected_Data).transform(selected_Data)
val encoder = new OneHotEncoder().setDropLast(false).setInputCol("jobIndex").setOutputCol("jobVec")
val encoded = encoder.transform(indexed) 
//下面代码为新增代码
val maritalIndexer=new StringIndexer().setInputCol("marital").setOutputCol("maritalIndex")
//注意:此处所使用的数据是job列应用OneHotEncoder算法后产生的数据encoded
//这里不能使用原始数据selected_Data,因为原始数据中没有jobVec列。
val maritalIndexed=maritalIndexer.fit(encoded).transform(encoded)
val maritalEncoder=new OneHotEncoder().setDropLast(false).setInputCol("maritalIndex").setOutputCol("maritalVec")
val maritalEncoded=maritalEncoder.transform(maritalIndexed)

val defaultIndexer=new StringIndexer().setInputCol("default").setOutputCol("defaultIndex")
//注意:此处所使用的数据是对marital列应用OneHotEncoder算法后产生的数据maritalEncoded
val defaultIndexed=defaultIndexer.fit(maritalEncoded).transform(maritalEncoded)
val defaultEncoder=new OneHotEncoder().setDropLast(false).setInputCol("defaultIndex").setOutputCol("defaultVec")
val defaultEncoded=defaultEncoder.transform(defaultIndexed)

val housingIndexer=new StringIndexer().setInputCol("housing").setOutputCol("housingIndex")
//注意:此处所使用的数据是对default列应用OneHotEncoder算法后产生的数据defaultEncoded
val housingIndexed=housingIndexer.fit(defaultEncoded).transform(defaultEncoded)
val housingEncoder=new OneHotEncoder().setDropLast(false).setInputCol("housingIndex").setOutputCol("housingVec")
val housingEncoded=housingEncoder.transform(housingIndexed)

val poutcomeIndexer=new StringIndexer().setInputCol("poutcome").setOutputCol("poutcomeIndex")
//注意:此处所使用的数据是对housing列应用OneHotEncoder算法后产生的数据housingEncoded
val poutcomeIndexed=poutcomeIndexer.fit(housingEncoded).transform(housingEncoded)
val poutcomeEncoder=new OneHotEncoder().setDropLast(false).setInputCol("poutcomeIndex").setOutputCol("poutcomeVec")
val poutcomeEncoded=poutcomeEncoder.transform(poutcomeIndexed)

val loanIndexer=new StringIndexer().setInputCol("loan").setOutputCol("loanIndex")
//注意:此处所使用的数据是对poutcome列应用OneHotEncoder算法后产生的数据poutcomeEncoded
val loanIndexed=loanIndexer.fit(poutcomeEncoded).transform(poutcomeEncoded)
val loanEncoder=new OneHotEncoder().setDropLast(false).setInputCol("loanIndex").setOutputCol("loanVec")
val loanEncoded=loanEncoder.transform(loanIndexed)
loanEncoded.show()
loanEncoded.printSchema()

上述代码对多个分类字段(marital、default、housing、poutcome、loan)使用了 StringIndex 和 OneHotEncoder 算法,代码执行流程可以用下图表示: 

æ­¤å¤è¾å¥å¾ççæè¿°

 

上图初看较复杂,其实很简单!我们对数据进行特征转换,实质上是对数据中的某列进行特征转换,每次应用特征转换算法都会有一个输入列和一个输出列。上图中,菱形表示应用算法后,增加了输出列的数据,正方形表示算法,正方形前后的箭头表示输入列和输出列。转换的具体步骤如下:

  • selected_Data 是我们选择了11列后的数据,首先对selected_Data数据中的job数据进行StringIndexer转换,算法输出一个新列:jobIndex,对应代码如下:
val indexer = new StringIndexer().setInputCol("job").setOutputCol("jobIndex")
val indexed = indexer.fit(selected_Data).transform(selected_Data)
  • indexed 变量中的数据是包含了新列 jobIndex 的数据,原始列 job 的类型为字符型,新列 jobIndex 的类型为对应的数值型,将此列输入到 OneHotEncoder 算法中,进行 OneHot 编码,输出一个新列 jobVec,对应代码如下:
val encoder = new OneHotEncoder().setDropLast(false).setInputCol("jobIndex").setOutputCol("jobVec")
val encoded = encoder.transform(indexed) 
  • encoded 变量中的数据是包含了新列 jobVec 的数据,接着用 encoded 数据中的 marital 列作为 StringIndexer 算法的输入列,算法输出一个新列 maritalIndex,对应代码如下:
val maritalIndexer=new StringIndexer().setInputCol("marital").setOutputCol("maritalIndex")
val maritalIndexed=maritalIndexer.fit(encoded).transform(encoded)
  • 接着对maritalIndexed变量中的maritalIndex列进行OneHotEncoder编码,其过程与job列一样,以此类推......
  • 最后对loan列进行特征转换,转换结果为loanEncoded数据,将loanEncoded数据中的内容和schema输出。

3.4.5 再次运行程序,查看输出结果:

æ­¤å¤è¾å¥å¾ççæè¿°

æ­¤å¤è¾å¥å¾ççæè¿°

 可以看出,marital、default、housing、poutcome、loan 这5个分类的字段增加情况和 job 字段一样,都增加了对应稀疏向量字段。

到目前为止,除了目标变量y,所有的字符型列都完成了 OneHot 编码,在对目标变量y进行数据转换后,就可以基于编码后的向量字段应用逻辑回归算法进行预测了。 在应用逻辑回归算法之前,我们使用 pipeline 技术对算法和数据进行流水线式组装,然后再应用逻辑回归算法。

3.5 建立逻辑回归模型进行预测

3.5.1 在上一步骤main方法中已有代码后面,继续编写代码:

//实例化一个向量组装器对象,
//将向量类型字段("jobVec","maritalVec", "defaultVec","housingVec","poutcomeVec","loanVec")
//和数值型字段("age","duration","previous","empvarrate")
//形成一个新的字段:features,其中包含了所有的特征值
val vectorAssembler = new VectorAssembler()
.setInputCols(Array("jobVec","maritalVec", "defaultVec","housingVec","poutcomeVec","loanVec","age","duration","previous","empvarrate"))
.setOutputCol("features")

//对目标变量进行StringIndexer特征转换,输出新列:label
 val indexerY = new StringIndexer().setInputCol("y").setOutputCol("label")
 //将特征算法按顺序进行合并,形成一个算法数组
 val transformers=Array(indexer,
                        encoder,
                        maritalIndexer,
                        maritalEncoder,
                        defaultIndexer,
                        defaultEncoder,
                        housingIndexer,
                        housingEncoder,
                        poutcomeIndexer,
                        poutcomeEncoder,
                        loanIndexer,
                        loanEncoder,
                        vectorAssembler,
                        indexerY);
//将原始数据selected_Data进行8-2分,80%用于训练数据。20%用于测试数据,评估训练模型的精确度。
val splits = selected_Data.randomSplit(Array(0.8,0.2))
val training = splits(0).cache()
val test = splits(1).cache()
//实例化逻辑回归算法
val lr = new LogisticRegression()
//将算法数组和逻辑回归算法合并,传入pipeline对象的stages中,然后作用于训练数据,训练模型
var model = new Pipeline().setStages(transformers :+ lr).fit(training)
//将上一步的训练模型作用于测试数据,返回测试结果
var result = model.transform(test)
//显示测试结果集中的真实值、预测值、原始值、百分比字段
result.select("label", "prediction","rawPrediction","probability").show(10,false)
//创建二分类算法评估器,对测试结果进行评估
val evaluator = new BinaryClassificationEvaluator()  
var aucTraining = evaluator.evaluate(result)
println("aucTraining = "+aucTraining)

3.5.2 再次运行程序,查看运行结果:

æ­¤å¤è¾å¥å¾ççæè¿°

 

从运行结果中可以看出,输出的测试结果前10条数据,其中 probability 列值是[0,1]对应的发生概率,如果某个分类值出现的概率大于0.5,那么预测值 prediction 就是对应的那个分类值。 对20%测试集的预测精确度为:0.92363403285001,即:精确度为 90% 以上,说明我们训练的模型,预测的准确度还是很高的。注意:每次运行的精确度不会完全一样,有稍微的差别。

截至此操作完整代码如下:

package com.shiyanlou.algorithm

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.DoubleType
import org.apache.spark.ml.feature.StringIndexer
import org.apache.spark.ml.feature.OneHotEncoder
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.ml.Pipeline
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
import org.apache.spark.ml.feature.VectorAssembler

object LogisticRegressionTest2 {
   def main(args: Array[String]): Unit = {
    val spark=SparkSession.builder().appName("Logistic_Prediction").master("local").getOrCreate()
    spark.sparkContext.setLogLevel("WARN")
    import spark.implicits._
    val bank_Marketing_Data=spark.read
                                 .option("header", true)
                                 .option("inferSchema", "true")   
                                 .csv("/opt/train/bank_marketing_data.csv")
    bank_Marketing_Data.show(5)
    val selected_Data=bank_Marketing_Data.select("age", 
                                                "job",
                                                "marital", 
                                                "default", 
                                                "housing", 
                                                "loan", 
                                                "duration",
                                                "previous", 
                                                "poutcome", 
                                                "empvarrate", 
                                                "y")
                                                .withColumn("age", bank_Marketing_Data("age").cast(DoubleType))
                                                .withColumn("duration", bank_Marketing_Data("duration").cast(DoubleType))
                                                .withColumn("previous", bank_Marketing_Data("previous").cast(DoubleType))

    val indexer = new StringIndexer().setInputCol("job").setOutputCol("jobIndex")
    val indexed = indexer.fit(selected_Data).transform(selected_Data)
    indexed.printSchema()
    indexed.show
    val encoder = new OneHotEncoder().setDropLast(false).setInputCol("jobIndex").setOutputCol("jobVec")
    val encoded = encoder.transform(indexed) 
    encoded.show()
    encoded.printSchema()
    val maritalIndexer=new StringIndexer().setInputCol("marital").setOutputCol("maritalIndex")
    val maritalIndexed=maritalIndexer.fit(encoded).transform(encoded)
    val maritalEncoder=new OneHotEncoder().setDropLast(false).setInputCol("maritalIndex").setOutputCol("maritalVec")
    val maritalEncoded=maritalEncoder.transform(maritalIndexed)

    val defaultIndexer=new StringIndexer().setInputCol("default").setOutputCol("defaultIndex")
    val defaultIndexed=defaultIndexer.fit(maritalEncoded).transform(maritalEncoded)
    val defaultEncoder=new OneHotEncoder().setDropLast(false).setInputCol("defaultIndex").setOutputCol("defaultVec")
    val defaultEncoded=defaultEncoder.transform(defaultIndexed)

    val housingIndexer=new StringIndexer().setInputCol("housing").setOutputCol("housingIndex")
    val housingIndexed=housingIndexer.fit(defaultEncoded).transform(defaultEncoded)
    val housingEncoder=new OneHotEncoder().setDropLast(false).setInputCol("housingIndex").setOutputCol("housingVec")
    val housingEncoded=housingEncoder.transform(housingIndexed)

    val poutcomeIndexer=new StringIndexer().setInputCol("poutcome").setOutputCol("poutcomeIndex")
    val poutcomeIndexed=poutcomeIndexer.fit(housingEncoded).transform(housingEncoded)
    val poutcomeEncoder=new OneHotEncoder().setDropLast(false).setInputCol("poutcomeIndex").setOutputCol("poutcomeVec")
    val poutcomeEncoded=poutcomeEncoder.transform(poutcomeIndexed)

    val loanIndexer=new StringIndexer().setInputCol("loan").setOutputCol("loanIndex")
    val loanIndexed=loanIndexer.fit(poutcomeEncoded).transform(poutcomeEncoded)
    val loanEncoder=new OneHotEncoder().setDropLast(false).setInputCol("loanIndex").setOutputCol("loanVec")
    val loanEncoded=loanEncoder.transform(loanIndexed)
    loanEncoded.show()
    loanEncoded.printSchema()

    val vectorAssembler = new VectorAssembler()
    .setInputCols(Array("jobVec","maritalVec", "defaultVec","housingVec","poutcomeVec","loanVec","age","duration","previous","empvarrate"))
    .setOutputCol("features")

     val indexerY = new StringIndexer().setInputCol("y").setOutputCol("label")
     val transformers=Array(indexer,
                            encoder,
                            maritalIndexer,
                            maritalEncoder,
                            defaultIndexer,
                            defaultEncoder,
                            housingIndexer,
                            housingEncoder,
                            poutcomeIndexer,
                            poutcomeEncoder,
                            loanIndexer,
                            loanEncoder,
                            vectorAssembler,
                            indexerY);
    val splits = selected_Data.randomSplit(Array(0.8,0.2))
    val training = splits(0).cache()
    val test = splits(1).cache()
    val lr = new LogisticRegression()
    var model = new Pipeline().setStages(transformers :+ lr).fit(training)
    var result = model.transform(test)
    result.select("label", "prediction","rawPrediction","probability").show(10,false)
    val evaluator = new BinaryClassificationEvaluator()  
    var aucTraining = evaluator.evaluate(result)
    println("aucTraining = "+aucTraining)
   }
}

五、总结

本次实验,采用了项目中使用机器学习算法的基本步骤,演示了对银行营销数据的分析预测。主要包括如下步骤:

  • 获取银行营销数据
  • 分析营销数据结构
  • 概要分析数据字段内容
  • 对数据使用特征工程
  • 建立逻辑回归模型并预测

除了上述步骤,有时根据数据情况和具体场景,需要对数据进行清洗。数据清洗的方式有删除法、均值法等,后续实验中会详细介绍。

需要注意的是:在具体项目中,如果算法工程师拿到的数据已经是数值型内容,或者已经进行了特征化,那么此时,可以省略相应步骤,直接建立相应的算法模型,进行预测。

https://www.shiyanlou.com/courses/1003/labs/4242/document

基于Spark技术的银行客户数据分析 大数据实验教学系统案例:银行客户数据分析某银行积累有大量客户数据,现希望大数据分析团队使用Spark技术对这些数据进行分析,以期获得有价值的信息。本案例用到的数据集说明如下:   数据集文件:/data/dataset/bank-full.csv   该数据集包含银行客户信息,其中部分字段的说明如下:1、启动HDFS集群   在Linux终端窗口下,输入以下命令,启动HDFS集群: 2、启动Spark集群   在Linux终端窗口下,输入以下命令,启动Spark集群: 3、启动zeppelin服务器   在 阅读详情

相关推荐

SparkML机器学习实战:应用回归算法,预测二手房价格

受某房产中介委托,需开发一套机器学习系统,当用户将要售卖的二手房挂到二手房网站上时,该机器学习系统能自动根据该二手房的相关信息给出合理的销售价格预测,以指导客户报价。本案例所使用的数据集为纯文本文件,说明如下:   数据集路径:/data/dataset/ml/house/train.csv1、启动HDFS集群   在Linux终端窗口下,输入以下命令,启动HDFS集群: 2、启动Spark集群   在Linux终端窗口下,输入以下命令,启动Spark集群: 3、启动zeppelin服务器   在Linux

需要远程指导仿真实验、代码有问题的,请后台私信或者关注公众号 2759

spark 3.4.4 机器学习基于逻辑回归算法及管道流实现鸢尾花分类预测案例

Pipeline将标签索引化、文本特征提取(词向量转换)以及逻辑回归模型训练这几个步骤有序地组合起来,实现了一个简单的文本分类任务流程,体现了Pipeline在整合机器学习流程方面的便利性和实用性。Spark 3.4.4

pblh123的专栏 1672

机器学习实战10-基于spark大数据技术与机器学习的结合应用实战

大家好,我是微学AI,今天给大家介绍一下机器学习实战10-基于spark大数据技术与机器学习的结合应用实战,Spark是一种快速、通用的大数据处理框架。它是由加州大学伯克利分校AMPLab开发。Spark提供了一个分布式计算的平台,可以在集群中并行处理大规模的数据集。

微学AI的博客 2834

逻辑回归实战——银行贷款案例初步实现

逻辑回归是一种简单而有效的分类算法,常用于预测二分类问题。它通过线性组合特征并使用Sigmoid函数将结果转化为概率,判断样本属于某一类的可能性。模型通过最小化对数损失函数来优化参数,通常采用梯度下降法进行求解。尽管名字带有“回归”,但它主要用于分类任务,是机器学习中的基础算法之一。

weixin_68114439的博客 597

MarketingDataAnalysis:存储库包含用于银行营销数据分析的代码

银行营销数据分析 数据 与葡萄牙银行机构的直接营销活动有关。 市场营销活动以电话为基础。 为了访问产品(银行定期存款)是否(“是”)订阅(“否”),通常需要与同一客户进行多次联系。 有数据集: bank-additional-full.csv,包含所有示例(41188)和20个输入,按日期排序(从2008年5月到2010年11月),非常接近[Moro等人,2014]中分析的数据 bank-additional.csv,其中包含10%的示例(4119)(从1)中随机选择)和20个输入。 bank-full.csv,包含所有示例和17个输入,按日期排序(此数据集的旧版本,输入较少)。 bank.csv,其中包含10%的示例和17个输入,它们是从3个(此数据集的旧版本,输入较少)中随机选择的。 提供了最小的数据集以测试更多对计算有要求的机器学习算法(例如SVM)。 目标 预测潜在客户,如

基于Spark的数据处理分析系统的设计与实现

基于Spark的数据处理分析系统的设计与实现随着计算机和信息技术的迅猛发展和普及应用,行业应用系统的规模迅速扩大,行业应用所产生的数据呈爆炸性增长。寻求有效的大数据处理技术、方法和手段已经成为现实世界...

python算法设计思想_提到算法就头大,12个算法设计项目给你练练脑

1. C 语言程序设计实验地址: http s:// www.shiyanlou.com/courses/1649学习人数: 308关注人数: 12本课程讲解 C 语言的开发环境以及对 C 语言的剖析,引入大量的 C 语言程序案例,把算法和语法结合起来,通过引导大家由浅入深地编写 C 程序,让大家掌握 C 语言。我们将从中学会 C 语言语法、数组、模块化程序设计指针、文件的输入与输出等。2. Go...

weixin_35829704的博客 292

银行计算机储蓄系统项目分析报告

银行储蓄系统前期分析 包括项目背景,数据流图,数据字典等

MLlib 二分类问题

MLlib 二分类问题 【课程性质:PySpark机器学习】 文章目录1. 实验目标2. 本次实验主要使用的 PythonPythonPython 库3. 适用的对象4. 实验步骤步骤1 安装并引入必要的库步骤2 探索数据步骤3 查看前五个观测样本3.1 pandas.DataFrame 比 Spark DataFrame.show()更漂亮3.2 查看数据标签3.3 数值变量的汇总统计信息3.4 变量的相关性检验3.5 为机器学习准备数据3.6 管道步骤4 逻辑回归步骤5 决策树分类器步骤6 随机森林分

shenhao-stu的博客 1037

naive bayes代码实现(python版)

这是我自己写的一个朴素贝叶斯的python代码,应该是比较直观易懂的

银行业数据挖掘案例 银行智能决策支持系统 客户挖掘 客户分析 共33页.pdf

当前的金融危机暴露出信息化存在如下问题: (1)IT治理不完善:高管层对信息系统建设及风险管控的重视和研究不够,决策和监督机制缺位。 (2)信息化战略规划不明晰:缺乏连贯性,规划布局不尽合理。 (3)信息化建设滞后于业务发展:信息系统平台千差万别,内 部应用系统难以实现信息共享,对数据的分析和利用难以 满足“以客户为中心”经营模式的需要。 (4)软硬件及核心技术受制于人:对系统安全造成隐患,甚至可能危及行业健康发展。 【客户价值评价指标体系】 主要包括:客户特征、行为、价值评价体系 分析方法:通过对客户基本资料、客户存取贷款记录、客户金融产品购买等信息数据进行深入分析,建立客户的群体划分标准,以针对不同客户群体进行针对性营销;建立客户消费行为的分析模型和评价指标体系,以进行客户风险管理;建立客户价值评估模型和指标体系,以对客户进行分级管理。

2026大数据专业毕设选题宝典:50个Hadoop+Spark项目详解,导师最爱的数据分析题目推荐

2026大数据专业毕设选题宝典:50个Hadoop+Spark项目详解,导师最爱的数据分析题目推荐

计算机编程果茶熊的博客 600

Bank Marketing Dataset银行营销数据集-数据集

Bank Marketing Dataset is related with direct marketing campaigns (phone calls) of a Portuguese banking institution.本数据集源自葡萄牙银行机构的一项电话营销活动。 bank-full.csv bank.csv bank-additional.csv bank-additional-full.csv

银行AI岗笔试全解析:从计算机基础到业务场景实战

在金融科技领域,算法工程师的招聘考核早已超越纯模型调参,转向对计算机基础、机器学习原理与业务场景理解的多维考察。以银行AI岗笔试为例,其题目设计往往融合TCP/IP、数据结构、集成学习等基础概念,并通过信用卡反欺诈、信用评分等真实业务场景,检验候选人端到端的方案设计能力。理解朴素贝叶斯、SVM、XGBoost等算法的本质差异,掌握哈希表与数组结合实现O(1)操作等编程技巧,是应对笔试的关键。这类考试的价值在于筛选出既懂技术又能落地业务的人才。本文以一份2019年招行信用卡中心AI方向笔试题为样本,系统拆解题

weixin_30719711的博客 432

深度强化学习系列(13): 策略梯度(Policy Gradient)

对于DQN来说使用一个网络直接逼近了值函数,最后取得了非常不错的效果, 但是对于一些连续性控制或者动作空间特别大的环境来说,很难全部计算所有的值函数来得到最好的策略,那么直接计算策略的方法就别提出来了。 回顾一下前面几篇,所谓的策略,其实就是状态到动作的映射数学表达为 π:s→aπ:s→a\pi : s \to a, 我们用ττ\tau 表示状态s到最后一个状态的一个序列,表示为: τ:st...

深度强化学习(DeepRL)探索博客 4641

MySQL根据idb文件恢复数据

恢复前提 MySQL 5.* 和 8.* 的恢复方式大同小异,最基本的区别是8.* 中,不再有表结构frm文件,所以要恢复 8.* 的数据,就必须要有完整的建表语句。 而恢复 5.* 的数据,就需要idb文件和 frm文件,当然,如果有完整的建表语句的话,不用frm文件也行。 恢复步骤 需要注意的是,以下操作需要选定数据库后执行。 5.* 恢复表结构 确定表列数 首先创建一张 与需要恢复数据的表 的名字相同的新表,如果已知原表的字段数量的话,就按照原表字段数建表,字段名和类型都不用考虑,如果不知道数量的话,

weixin_45121446的博客 5447

招行信用卡中心数据挖掘笔试复盘:题型拆解与备考方向

数据挖掘岗位的笔试考察,往往不只是算法模型的堆砌,更看重候选人能否用数据解决实际业务问题。尤其在银行系场景下,统计基础、SQL能力、机器学习原理与业务理解构成了核心能力框架。SQL作为数据提取与特征工程的基础工具,几乎是必考项,窗口函数、多表连接和查询优化意识都是高频考点;机器学习部分则围绕逻辑回归、决策树、集成学习等经典算法,以及模型评估指标如AUC、精确率与召回率展开。统计学中的假设检验、概率分布与回归分析,则为风控模型和业务结论的可信度提供了理论支撑。这些技术能力最终要落到信用评分卡、用户画像、精准营

weixin_34235457的博客 547

电化学阻抗谱(EIS)在锂离子电池中的应用

摘要电化学阻抗谱(electrochemical impedance spectroscopy, EIS)是一种重要的电化学测试方法,在锂离子电池动力学反应机理研究及动力学参数测量等方面具有广泛的应用。在锂离子电池运行过程中,各反应步骤的动力学参数及时间常数不同,而EIS可以在较宽的频率范围内对体系施加小幅正弦激励信号,使得在特定频率下突出特定时间常数的电极过程,从而将复杂的电极过程分离,实现单一电极过程动力学的分析。通过对交流阻抗信息的研究,也可以为锂离子电池及其他电池的应用与管理、电化学过程设计及电极材

qiushireader的博客 2649

Spark 数据挖掘 - 利用决策树预测森林覆盖类型

Spark 数据挖掘—利用决策树预测森林覆盖类型 1 前言 预测问题记住一点:最垃圾的预测就是使用平均值,如果你的预测连比直接给出平均值效果都要差,那就省省吧! 统计学诞生一个多世纪之后,随着现在机器学习和数据科学的产生,我们依旧使用回归的思想来进行预测,尽管回归 就是用平均值向后不断回滚来预...

chengtuo5899的博客 236

54、生物神经网络建模全解析

本文系统解析了生物神经网络的建模方法,涵盖从单个神经元到神经网络的多个时空尺度。首先介绍神经元层面的生物物理模型(如霍奇金-赫胥黎模型和多隔室模型)与多种简化模型(如菲茨休-纳古莫、莫里斯-勒卡尔、积分发放模型等),并分析其适用场景。随后探讨突触层面的信号传递机制,对比电突触与化学突触在结构、传输速度和方向上的差异,阐述其在神经网络建模中的理论意义。文章旨在为理解神经系统的信息处理机制、构建受生物启发的人工智能模型提供理论基础。

app77的博客 76

【系统分析师】2009年真题:综合知识-答案及详解

在某银行业务的用例模型中,“取款”用例需要等到“存款”用例执行之后才能执行,两个用例之间的关系属于(1);“取款”和“存款”两个用例中都需 要执行查询余额的功能,将查询余额提取成独立的用例,那么“取款”和“存款” 用例与“查询余额”用例之间的关系属于(2)。(1)A. 关联关系 B. 扩展关系 C. 使用关系 D. 依赖关系 (2)A. 扩展关系 B. 使用关系 C. 依赖关系 D. 继承关系【参考答案】D、A雇员类含有计算报酬的行为,利用面向对象的(3),可以使得其派生类专职 雇员类和兼职雇员类计算报酬的

数据知道的博客 1万+

天云大数据_【案例分享】天云大数据最佳实践系列之——信用评分模型

本文为天云大数据原创大数据能力特有的性质,使其正在成为大型银行真正的核心竞争力。银行大数据能力表现在多方面,但大数据思维和数据挖掘能力是最关键、也是最重要的。天云大数据自成立以来,一直深耕于金融领域,积累了大量成功案例。从本期微信开始,分享天云在金融领域的最佳实践系列。大数据事关银行战略转型全局进入大数据时代,随着数据处理技术的进步和数据来源的迅速扩展,银行业的一切业务活动都被数字化,商业银行得以...

weixin_39912250的博客 383

基于Simulink的电机轴电压与轴电流抑制仿真

本文带你完成了电机轴电压与轴电流抑制的Simulink仿真,实现了:✅ 揭示了共模电压 → 寄生电容 → 轴电压 → 轴承电蚀的完整链路✅ 搭建了含寄生参数的高频电机等效模型共模电压最小化PWM(源头抑制)dv/dt滤波器(路径阻断)轴接地(终端泄放) ✅ 通过仿真验证了轴电压从68 V降至<3 V的显著效果 ✅ 为高可靠性电机系统提供低成本、高效益的轴承保护方案。

amy_mhd的博客 159

c++求区间第k大数_「K些项目」提到算法就头大,12个算法设计项目给你练练脑...

 对代码、编程感兴趣的可以关注「老K玩代码」公众号和我交流! 分享代码、经验、项目和资讯1. C 语言程序设计实验地址: http s:// www.shiyanlou.com/courses/1649学习人数: 308关注人数: 12本课程讲解 C 语言的开发环境以及对 C 语言的剖析,引入大量的 C 语言程序案例,把算法和语法结合起来,通过引导大家由浅入深地编写 C 程序,让大家掌握 C 语...

weixin_42511373的博客 270

【前端圭臬】二:你知道的和不知道的 HTML

关于 HTML,说几个你可能不太关注的问题。

Evan 的博客 4757

自适应控制 (Adaptive Control) 算法详解及案例分析

自适应控制是一种能够在线调整控制器参数以适应系统动态变化的控制方法。其核心思想是通过实时估计系统参数或调整控制器参数,使系统在不确定性或动态变化的环境中仍能保持稳定性和性能。

闲人编程的博客 4081
上一篇: ggplot2 快速入门:基础图形作法
下一篇: C++ 实现高性能内存池
git1314
博客等级 码龄9年 282粉丝 114原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值