PySpark(一)

PySpark(一)
其中Spark主要是由Scala语言开发,为了方便和其他系统集成而不引入scala相关依赖,部分实现使用Java语言开发,例如External Shuffle Service等。总体来说,Spark是由JVM语言实现,会运行在JVM中。然而,Spark除了提供Scala/Java开发接口外,还提供了Python、R等语言的开发接口,为了保证Spark核心实现的独立性,Spark仅在外围做包装,实现对不同语言的开发支持。
Spark运行时架构
首先我们先回顾下Spark的基本运行时架构,如下图所示,其中橙色部分表示为JVM,Spark应用程序运行时主要分为Driver和Executor,Driver负载总体调度及UI展示,Executor负责Task运行,Spark可以部署在多种资源管理系统中,例如Yarn、Mesos等,同时Spark自身也实现了一种简单的Standalone(独立部署)资源管理系统,可以不用借助其他资源管理系统即可运行。更多细节请参考Spark Scheduler内部原理剖析。

用户的Spark应用程序运行在Driver上(某种程度上说,用户的程序就是Spark Driver程序),经过Spark调度封装成一个个Task,再将这些Task信息发给Executor执行,Task信息包括代码逻辑以及数据信息,Executor不直接运行用户的代码。
PySpark运行时架构
为了不破坏Spark已有的运行时架构,Spark在外围包装一层Python API,借助Py4j实现Python和Java的交互,进而实现通过Python编写Spark应用程序,其运行时架构如下图所示。

其中白色部分是新增的Python进程,在Driver端,通过Py4j实现在Python中调用Java的方法,即将用户写的PySpark程序”映射”到JVM中,例如,用户在PySpark中实例化一个Python的SparkContext对象,最终会在JVM中实例化Scala的SparkContext对象;在Executor端,则不需要借助Py4j,因为Executor端运行的Task逻辑是由Driver发过来的,那是序列化后的字节码,虽然里面可能包含有用户定义的Python函数或Lambda表达式,Py4j并不能实现在Java里调用Python的方法,为了能在Executor端运行用户定义的Python函数或Lambda表达式,则需要为每个Task单独启一个Python进程,通过socket通信方式将Python函数或Lambda表达式发给Python进程执行。语言层面的交互总体流程如下图所示,实线表示方法调用,虚线表示结果返回。

下面分别详细剖析PySpark的Driver是如何运行起来的以及Executor是如何运行Task的。
Driver端运行原理
当我们通过spark-submmit提交pyspark程序,首先会上传python脚本及依赖,并申请Driver资源,当申请到Driver资源后,会通过PythonRunner(其中有main方法)拉起JVM,如下图所示。

PythonRunner入口main函数里主要做两件事:
o 开启Py4j GatewayServer
o 通过Java Process方式运行用户上传的Python脚本
用户Python脚本起来后,首先会实例化Python版的SparkContext对象,在实例化过程中会做两件事:
o 实例化Py4j GatewayClient,连接JVM中的Py4j GatewayServer,后续在Python中调用Java的方法都是借助这个Py4j Gateway
o 通过Py4j Gateway在JVM中实例化SparkContext对象
经过上面两步后,SparkContext对象初始化完毕,Driver已经起来了,开始申请Executor资源,同时开始调度任务。用户Python脚本中定义的一系列处理逻辑最终遇到action方法后会触发Job的提交,提交Job时是直接通过Py4j调用Java的PythonRDD.runJob方法完成,映射到JVM中,会转给sparkContext.runJob方法,Job运行完成后,JVM中会开启一个本地Socket等待Python进程拉取,对应地,Python进程在调用PythonRDD.runJob后就会通过Socket去拉取结果。
把前面运行时架构图中Driver部分单独拉出来,如下图所示,通过PythonRunner入口main函数拉起JVM和Python进程,JVM进程对应下图橙色部分,Python进程对应下图白色部分。Python进程通过Py4j调用Java方法提交Job,Job运行结果通过本地Socket被拉取到Python进程。还有一点是,对于大数据量,例如广播变量等,Python进程和JVM进程是通过本地文件系统来交互,以减少进程间的数据传输。

Executor端运行原理
为了方便阐述,以Spark On Yarn为例,当Driver申请到Executor资源时,会通过CoarseGrainedExecutorBackend(其中有main方法)拉起JVM,启动一些必要的服务后等待Driver的Task下发,在还没有Task下发过来时,Executor端是没有Python进程的。当收到Driver下发过来的Task后,Executor的内部运行过程如下图所示。

Executor端收到Task后,会通过launchTask运行Task,最后会调用到PythonRDD的compute方法,来处理一个分区的数据,PythonRDD的compute方法的计算流程大致分三步走:
o 如果不存在pyspark.deamon后台Python进程,那么通过Java Process的方式启动pyspark.deamon后台进程,注意每个Executor上只会有一个pyspark.deamon后台进程,否则,直接通过Socket连接pyspark.deamon,请求开启一个pyspark.worker进程运行用户定义的Python函数或Lambda表达式。pyspark.deamon是一个典型的多进程服务器,来一个Socket请求,fork一个pyspark.worker进程处理,一个Executor上同时运行多少个Task,就会有多少个对应的pyspark.worker进程。
o 紧接着会单独开一个线程,给pyspark.worker进程喂数据,pyspark.worker则会调用用户定义的Python函数或Lambda表达式处理计算。
o 在一边喂数据的过程中,另一边则通过Socket去拉取pyspark.worker的计算结果。
把前面运行时架构图中Executor部分单独拉出来,如下图所示,橙色部分为JVM进程,白色部分为Python进程,每个Executor上有一个公共的pyspark.deamon进程,负责接收Task请求,并fork pyspark.worker进程单独处理每个Task,实际数据处理过程中,pyspark.worker进程和JVM Task会较频繁地进行本地Socket数据通信。

总结
总体上来说,PySpark是借助Py4j实现Python调用Java,来驱动Spark应用程序,本质上主要还是JVM runtime,Java到Python的结果返回是通过本地Socket完成。虽然这种架构保证了Spark核心代码的独立性,但是在大数据场景下,JVM和Python进程间频繁的数据通信导致其性能损耗较多,恶劣时还可能会直接卡死,所以建议对于大规模机器学习或者Streaming应用场景还是慎用PySpark,尽量使用原生的Scala/Java编写应用程序,对于中小规模数据量下的简单离线任务,可以使用PySpark快速部署提交。

Ubuntu下安装pyspark
1 Resilient Distributed Datasets(RDD)
弹性分布式数据集(RDD)是一个不可变的JVM对象的分布式集合,是Spark的基本抽象。
1.1 创建RDD
准备工作:

import pyspark
from pyspark import SparkContext
from pyspark import SparkConf
conf = SparkConf().setAppName(‘project1’).setMaster(‘local’)
sc = SparkContext.getOrCreate(conf)

在PySpark里有两种方法创建RDD:
一是,.parallelize(…) 个collection集合 ( list or an array of some elements)。

data = sc.parallelize([(‘amber’,22),(‘alfred’,23),(‘skye’,4),(‘albert’,12),(‘amber’,9)])

二是,引用位于本地或HDFS上的某个文件(或多个文件)。

data_from_file = sc.textFile(’/home/qml/pyspark-ex/VS14MORT.txt.gz’,4)

sc.textFile(…,n)中的最后一个参数指定数据集被分区的数量,经验是分成两个四分区

Spark 支持多种数据格式:可以使用JDBC驱动程序读取文本,Parquet,JSON,Hive表和来自关系数据库的数据。请注意,Spark可以自动处理压缩的数据集(如Gzip压缩数据集)。
从文件读取的数据表示为MapPartitionsRDD,而不是像当我们.paralellize(…)一个集合的数据一样表示为ParallelCollectionRDD。

1.2 Schema

RDD是无模式的数据结构(不像DataFrames)。因此,在使用RDD时,并行化数据集对于Spark来说是完美的。

data_heterogenous = sc.parallelize([(‘Ferrari’,‘fast’),{‘Porsche’:100000},[‘Spain’,‘visited’,4504]]).collect()
1
2
所以,我们可以混合几乎任何东西:一个元组,一个字典,或一个列表。
一旦你.collect()数据集(即,运行一个动作将其返回给驱动程序),你可以像在Python中通常那样访问对象中的数据:

data_heterogenous[1][‘Porsche’]
100000
.collect()方法将RDD的所有元素返回到驱动程序,并将其作为列表序列化。

1.3 读取文件

从文本文件读取时,文件中的每一行形成RDD的一个元素。 可以创建一个元素列表,每行代表一个值列表。

data_from_file.take(1)

1.4 Lambda表达式
1.4.1 Transformations
.map(…)
该方法应用于RDD的每个元素。
data_2014_2 = data_from_file_conv.map(lambda row: (row[16], int(row[16])))
data_2014_2.take(10)
.filter(…)
允许选择符合指定条件的数据集元素。

data_filtered = data_from_file_conv.filter(lambda row: row[5] == ‘F’ and row[21] == ‘0’)
data_filtered.count()
.flatMap(…)
与map()的工作方式类似,但返回的是平铺的结果而不是列表。
In [3]:
data_2014_flat = data_from_file_conv.flatMap(lambda row: (row[16], int(row[16]) + 1))
data_2014_flat.take(10)
.distinct()
此方法返回指定列中不同值的列表。

In [5]:
distinct_gender = data_from_file_conv.map(lambda row: row[5]).distinct().collect()
distinct_gender

.sample(…)
该方法返回数据集中的随机样本。

fraction = 0.1
data_sample = data_from_file_conv.sample(False, fraction, 666)

data_sample.take(1)

.leftOuterJoin(…)
左外连接就像SQL一样,根据两个数据集中的值加入两个RDD,并从左RDD中返回从右侧追加两个RDD匹配的记录。

rd1 = sc.parallelize([(‘a’,1),(‘b’,4),(‘c’,10)])
rd2 = sc.parallelize([(‘a’,4),(‘a’,1),(‘b’,‘6’),(‘d’,15)])
rd3 = rd1.leftOuterJoin(rd2)
print rd3.take(5)

如果我们使用.join(…)方法,那么当这两个值在这两个RDD之间相交时,我们只能得到’a’和’b’的值。

rd4 = rd1.join(rd2)
print rd4.collect()
[(‘a’, (1, 4)), (‘a’, (1, 1)), (‘b’, (4, ‘6’))]

另一个有用的方法是.intersection(…),它返回两个RDD中相同的记录。

rd5 = rd1.intersection(rd2)
print rd5.collect()
[(‘a’, 1)]

1.4.2 Actions

.take(…)
该方法从单个数据分区返回n个最高行。

In [9]:
data_first = data_from_file_conv.take(1)
data_first

.reduce(…)

rd1.map(lambda row: row[1]).reduce(lambda x,y:x+y)

.reduceByKey(…)

data_key = sc.parallelize([(‘a’, 4),(‘b’, 3),(‘c’, 2),(‘a’, 8),(‘d’, 2),(‘b’, 1),(‘d’, 3)],4)
data_key.reduceByKey(lambda x, y: x + y).collect()
[(‘b’, 4), (‘c’, 2), (‘a’, 12), (‘d’, 5)]

.count()
统计RDD中元素的数量。

data_reduce.count()
.countByKey()
如果你的数据集是键值的形式,则可以使用.countByKey()方法获取不同键的数量。

data_key.countByKey().items()
[(‘a’, 2), (‘b’, 2), (‘d’, 2), (‘c’, 1)]
.saveAsTextFile(…)
将RDD保存到文本文件:每个分区保存到一个单独的文件。

data_key.saveAsTextFile(’/Users/drabast/Documents/PySpark_Data/data_key.txt’)
1
.foreach(…)
一种将函数应用同到RDD每个元素的迭代法。

def f(x):
print(x)
data_key.foreach(f)

2 DataFrames
和Python的dataframe类似,pyspark也有dataframe,其处理速度远快于无结构的RDD。

Spark 2.0 用 SparkSession 代替了SQLContext。各种 Spark contexts,包括:
HiveContext, SQLContext, StreamingContext,以及SparkContext
全部合并到了SparkSession中,以后仅用这一个包作为读取数据的入口。

2.1 创建 DataFrames
准备工作:

import pyspark
from pyspark.sql import SparkSession
spark = SparkSession.builder
.appName(“Python Spark SQL basic example”)
.config(“spark.some.config.option”, “some-value”)
.getOrCreate()

sc = spark.sparkContext
首先创建一个 stringJSONRDD的 RDD,然后将它转为一个DataFrame。

stringJSONRDD = sc.parallelize((’’’
… {‘id’:‘123’,
… ‘name’:‘Katie’,
… ‘age’:19,
… ‘eyeColor’:‘brown’}’’’,
… ‘’’{‘id’,‘234’,
… ‘name’:‘Michael’,
… ‘age’:22,
… ‘eyeColor’:‘green’}’’’,
… ‘’’{‘id’:‘345’,
… ‘name’:‘Simone’,
… ‘age’:23,
… ‘eyeColor’:‘blue’}’’’))
3 Prepare Data for Modeling
所有的数据都是脏的,不管是从网上下载的数据集,或其他来源。直到你测试和证明你的数据处于干净状态才能用来建模。因此,为了建模需要清理数据集,还需要检查数据集的特征分布,并确认它们符合预定义的标准。

3.1 检查重复项、缺失值和异常值

重复项
生成一个简单的dataframe如下:

df = spark.createDataFrame([
… (1,144.5,5.9,33,‘M’),
… (2,167.2,5.4,45,‘M’),
… (3,124.1,5.2,23,‘F’),
… (4,144.5,5.9,33,‘M’),
… (5,133.2,5.7,54,‘F’),
… (3,124.1,5.2,23,‘F’),
… (5,129.2,5.3,42,‘M’),
… ],[‘id’,‘weight’,‘height’,‘age’,‘gender’])
显然,这个数据只有几行,一眼就可以发现是否有重复值。但对于百万级别的数据呢?
第一件事,就是用.distinct()方法检查。

print(‘count of rows: {0}’.format(df.count()))
count of rows: 7

print(‘count of distinct rows: {0}’.format(df.distinct().count()))
count of distinct rows: 6
可以用.dropna(…)方法删除缺失值,用.fillna(…) 方法替换缺失值。

means = df_miss_no_income.agg(*[fn.mean©.alias© for c in df_miss_no_income.columns if c != ‘gender’]).toPandas().to_dict(‘records’)[0]
means[‘gender’]=‘missing’
df_miss_no_income.fillna(means).show()

异常值
异常值是那些明显偏离样本其余部分分布的观测值。

一般定义为,如果所有的值大致在Q1-1.5IQR和Q3 + 1.5IQR范围内,那么可以认为没有异常值,其中IQR是四分位间距; IQR被定义为第三四分位数Q3与第一四分位数Q1的差距。

备注:

第一四分位数 (Q1),又称“较小四分位数”,等于该样本中所有数值由小到大排列后第25%的数字。
第二四分位数 (Q2),又称“中位数”,等于该样本中所有数值由小到大排列后第50%的数字。
第三四分位数 (Q3),又称“较大四分位数”,等于该样本中所有数值由小到大排列后第75%的数字。

df_outliers = spark.createDataFrame([
… ], [‘id’, ‘weight’, ‘height’, ‘age’])
用 .approxQuantile(…) 方法计算四分位数,指定的第一个参数是列的名称,第二个参数可以是介于0或1之间的数字(其中0.5意味着计算中位数)或列表(在我们的例子中),第三个参数指定可接受的度量的误差(如果设置为0,它将计算度量的精确值,但这样做可能会非常耗资源)。

cols = [‘weight’,‘height’,‘age’]
bounds={}
for col in cols:
… quantiles = df_outliers.approxQuantile(col,[0.25,0.75],0.05)
… IQR = quantiles[1]-quantiles[0]
… bounds[col] = [quantiles[0]-1.5IQR,quantiles[1]+1.5IQR]

筛选出异常值:

outliers = df_outliers.select(*[‘id’] + [
(
(df_outliers[c] < bounds[c][0]) |
(df_outliers[c] > bounds[c][1])
).alias(c + ‘_o’) for c in cols
])
outliers.show()
df_outliers = df_outliers.join(outliers, on=‘id’)
df_outliers.filter(‘weight_o’).select(‘id’, ‘weight’).show()
df_outliers.filter(‘age_o’).select(‘id’, ‘age’).show()
3.2 描述性统计

按逗号切割,并将每个元素转换为一个整数:

sc = spark.sparkContext
fraud = sc.textFile(‘ccFraud.csv.gz’)
header = fraud.first()
fraud = fraud.filter(lambda row: row != header).map(lambda row: [int(elem) for elem in row.split(’,’)])
创建dataframe的schema:

fields = [typ.StructField(h[1:-1],typ.IntegerType(),True) for h in header.split(’,’)]
schema = typ.StructType(fields)
创建dataframe:

fraud_df = spark.createDataFrame(fraud,schema)
查看schema:

fraud_df.printSchema()
root
|-- custID: integer (nullable = true)
|-- gender: integer (nullable = true)
|-- state: integer (nullable = true)
|-- cardholder: integer (nullable = true)
|-- balance: integer (nullable = true)
|-- numTrans: integer (nullable = true)
|-- numIntlTrans: integer (nullable = true)
|-- creditLine: integer (nullable = true)
|-- fraudRisk: integer (nullable = true)
用.groupby(…)方法分组统计:

fraud_df.groupby(‘gender’).count().show()
±-----±-----+
|gender|count |
±-----±-----+
| 1 |6178231|
| 2 |3821769|
±-----±-----+
用 .describe()方法对数值进行描述性统计:

numerical = [‘balance’, ‘numTrans’, ‘numIntlTrans’]
desc = fraud_df.describe(numerical)
desc.show()
1
2
3

从上面的描述性统计可以看出两点:

1)所有的特征都是正倾斜的,最大值是平均数的几倍。
2)离散系数(coefficient of variation,或变异系数)非常高,接近甚至超过1,说明数据的离散程度很大,波动范围很大。

备注:

正倾斜(positively skewed): 平均数 > 中位数,由于数据中有些很大很大的极端值,使得整体平均数被极少数的极端大值拉大了,俗称“被平均”,而中位数受极端值的影响其实很小,因而此时用中位数作为中心趋势的估计比较稳健。
负倾斜:同理。
离散系数 = 标准差 / 平均值
检查某个特征的偏度:

fraud_df.agg({‘balance’: ‘skewness’}).show()
1

常用其他函数包括:avg() , count() , countDistinct() , first() , kurtosis() , max() , mean() , min() , skewness() , stddev() , stddev_pop() , stddev_samp() , sum() , sumDistinct() , var_pop() , var_samp() and variance() 等。

特征间相互关系的另一个非常有用的度量是相关性(correlation)。

你的模型通常应该只包括那些与你的目标高度相关的特征。然而,检查这些特征之间的相关性几乎是同等重要的,最好选择的是特征之间几乎不相关,而同时特征与目标高度相关。

只要数据是DataFrame格式,在PySpark中计算相关性非常容易。唯一的困难是.corr(…)方法现在支持Pearson相关系数,而它只能计算成对的相关性,如下:

fraud_df.corr(‘balance’, ‘numTrans’)
1
创建一个相关矩阵:

n_numerical = len(numerical)
corr = []
for i in range(0, n_numerical):
temp = [None] * i
for j in range(i, n_numerical):
temp.append(fraud_df.corr(numerical[i], numerical[j]))
corr.append(temp)

可以看见特征之间几乎不存在相关性,因此,所有的特征都能用到我们的模型中。

3.3 可视化

准备工作:

%matplotlib inline
import matplotlib.pyplot as plt
直方图(Histograms) 是评估特征分布最简单的方法。
用pyspark有三种方法生成直方图:

汇总workers中的数据,并返回一个汇总的bin列表,并在直方图的每个bin中计数给driver。
将所有数据返回给driver,并允许绘图库的方法为你完成这项工作。
对数据进行采样,然后将它们返回给driver进行绘图。
如果数据是几百万行,第二种方法显然不可取。因此需要先聚合数据。

hists = fraud_df.select(‘balance’).rdd.flatMap(
lambda row: row
).histogram(20)

data = {
‘bins’: hists[0][:-1],
‘freq’: hists[1]
}
plt.bar(data[‘bins’], data[‘freq’], width=2000)
plt.title(‘Histogram of ‘balance’’)

http://www.tomdrabas.com/site/blog
Introducing MLlib
MLlib 即Machine Learning Library。
4.1 载入数据并转换数据
数据集下载:births_train.csv.gz.。
创建数据集的schema:

import pyspark.sql.types as typ

labels = [
(‘INFANT_ALIVE_AT_REPORT’, typ.StringType()),
(‘BIRTH_YEAR’, typ.IntegerType()),
(‘BIRTH_MONTH’, typ.IntegerType()),
(‘BIRTH_PLACE’, typ.StringType()),
(‘MOTHER_AGE_YEARS’, typ.IntegerType()),
(‘MOTHER_RACE_6CODE’, typ.StringType()),
(‘MOTHER_EDUCATION’, typ.StringType()),
(‘FATHER_COMBINED_AGE’, typ.IntegerType()),
(‘FATHER_EDUCATION’, typ.StringType()),
(‘MONTH_PRECARE_RECODE’, typ.StringType()),
(‘CIG_BEFORE’, typ.IntegerType()),
(‘CIG_1_TRI’, typ.IntegerType()),
(‘CIG_2_TRI’, typ.IntegerType()),
(‘CIG_3_TRI’, typ.IntegerType()),
(‘MOTHER_HEIGHT_IN’, typ.IntegerType()),
(‘MOTHER_BMI_RECODE’, typ.IntegerType()),
(‘MOTHER_PRE_WEIGHT’, typ.IntegerType()),
(‘MOTHER_DELIVERY_WEIGHT’, typ.IntegerType()),
(‘MOTHER_WEIGHT_GAIN’, typ.IntegerType()),
(‘DIABETES_PRE’, typ.StringType()),
(‘DIABETES_GEST’, typ.StringType()),
(‘HYP_TENS_PRE’, typ.StringType()),
(‘HYP_TENS_GEST’, typ.StringType()),
(‘PREV_BIRTH_PRETERM’, typ.StringType()),
(‘NO_RISK’, typ.StringType()),
(‘NO_INFECTIONS_REPORTED’, typ.StringType()),
(‘LABOR_IND’, typ.StringType()),
(‘LABOR_AUGM’, typ.StringType()),
(‘STEROIDS’, typ.StringType()),
(‘ANTIBIOTICS’, typ.StringType()),
(‘ANESTHESIA’, typ.StringType()),
(‘DELIV_METHOD_RECODE_COMB’, typ.StringType()),
(‘ATTENDANT_BIRTH’, typ.StringType()),
(‘APGAR_5’, typ.IntegerType()),
(‘APGAR_5_RECODE’, typ.StringType()),
(‘APGAR_10’, typ.IntegerType()),
(‘APGAR_10_RECODE’, typ.StringType()),
(‘INFANT_SEX’, typ.StringType()),
(‘OBSTETRIC_GESTATION_WEEKS’, typ.IntegerType()),
(‘INFANT_WEIGHT_GRAMS’, typ.IntegerType()),
(‘INFANT_ASSIST_VENTI’, typ.StringType()),
(‘INFANT_ASSIST_VENTI_6HRS’, typ.StringType()),
(‘INFANT_NICU_ADMISSION’, typ.StringType()),
(‘INFANT_SURFACANT’, typ.StringType()),
(‘INFANT_ANTIBIOTICS’, typ.StringType()),
(‘INFANT_SEIZURES’, typ.StringType()),
(‘INFANT_NO_ABNORMALITIES’, typ.StringType()),
(‘INFANT_ANCEPHALY’, typ.StringType()),
(‘INFANT_MENINGOMYELOCELE’, typ.StringType()),
(‘INFANT_LIMB_REDUCTION’, typ.StringType()),
(‘INFANT_DOWN_SYNDROME’, typ.StringType()),
(‘INFANT_SUSPECTED_CHROMOSOMAL_DISORDER’, typ.StringType()),
(‘INFANT_NO_CONGENITAL_ANOMALIES_CHECKED’, typ.StringType()),
(‘INFANT_BREASTFED’, typ.StringType())
]

schema = typ.StructType([
typ.StructField(e[0], e[1], False) for e in labels
])

About This Book, Learn why and how you can efficiently use Python to process data and build machine learning models in Apache Spark 2.0Develop and deploy efficient, scalable real-time Spark solutionsTake your understanding of using Spark with Python to the next level with this jump start guide, Who This Book Is For, If you are a Python developer who wants to learn about the Apache Spark 2.0 ecosystem, this book is for you. A firm understanding of Python is expected to get the best out of the book. Familiarity with Spark would be useful, but is not mandatory., What You Will Learn, Learn about Apache Spark and the Spark 2.0 architectureBuild and interact with Spark DataFrames using Spark SQLLearn how to solve graph and deep learning problems using GraphFrames and TensorFrames respectivelyRead, transform, and understand data and use it to train machine learning modelsBuild machine learning models with MLlib and MLLearn how to submit your applications programmatically using spark-submitDeploy locally built applications to a cluster, In Detail, Apache Spark is an open source framework for efficient cluster computing with a strong interface for data parallelism and fault tolerance. This book will show you how to leverage the power of Python and put it to use in the Spark ecosystem. You will start by getting a firm understanding of the Spark 2.0 architecture and how to set up a Python environment for Spark., You will get familiar with the modules available in PySpark. You will learn how to abstract data with RDDs and DataFrames and understand the streaming capabilities of PySpark. Also, you will get a thorough overview of machine learning capabilities of PySpark using ML and MLlib, graph processing using GraphFrames, and polyglot persistence using Blaze. Finally, you will learn how to deploy your applications to the cloud using the spark-submit command., By the end of this book, you will have established a firm understanding of the Spark Python API and how it can be used t
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ljtyxl

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

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

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

打赏作者

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

抵扣说明:

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

余额充值