RDD和SparkSQL综合应用

PySpark中RDD的转换操作(转换算子) 在PySpark中,转换操作(转换算子)返回的结果通常是一个RDD对象或DataFrame对象或迭代器对象,具体返回类型取决于转换操作(转换算子)的类型和参数。在PySpark中,RDD提供了多种转换操作(转换算子),用于对元素进行转换和操作。函数来判断转换操作(转换算子)的返回类型,并使用相应的方法来处理返回结果。如果需要确定转换操作(转换算子)的返回类型,可以使用Python内置的。类似地,对于一个DataFrame对象,可以使用以下代码来判断。例如,对于一个包含整数的RDD,可以使用以下代码来判断。 阅读详情

公众号后台回复关键词:pyspark,获取本项目github地址。

 

在pyspark大数据项目实践中,我们往往要综合应用SparkSQL和RDD来完成任务。

通常,我们会使用SparkSQL的DataFrame来负责项目中数据读写相关的任务。

对于一些能够表达为表合并,表拼接,表分组等常规SQL操作的任务,我们也自然倾向于使用DataFrame来表达我们的逻辑。

但在一些真实项目场景中,可能会需要实现一些非常复杂和精细的逻辑,我们不知道如何使用DataFrame来直接实现这些逻辑。

或者使用DataFrame来实现它们过于复杂,不是简单地写几个自定义函数就可以。

我们往往会将DataFrame转化为RDD,在RDD中应用Python中的列表和字典等数据结构的操作来实现这个逻辑,然后再将RDD转回成DataFrame。

下面以一个DBSCAN聚类算法的分布式实现为例,来说明综合应用SparkSQL和RDD的方法。

这个案例的难度是工业级的,读者不一定能够完全理解,不用过分担心。

我相信,即使阅读这个案例后仅理解很少的一部分,也会让读者对Spark在真实项目场景中的应用技巧建立起更好的感觉。

import findspark

#指定spark_home为刚才的解压路径,指定python路径
spark_home = "/Users/liangyun/ProgramFiles/spark-3.0.1-bin-hadoop3.2"
python_path = "/Users/liangyun/anaconda3/bin/python"
findspark.init(spark_home,python_path)

import pyspark 
from pyspark.sql import SparkSession
from pyspark.storagelevel import StorageLevel 

#SparkSQL的许多功能封装在SparkSession的方法接口中

spark = SparkSession.builder \
        .appName("dbscan") \
        .config("master","local[4]") \
        .enableHiveSupport() \
        .getOrCreate()

sc = spark.sparkContext

一,DBSCAN算法简介

DBSCAN是一种非常著名的基于密度的聚类算法。其英文全称是 Density-Based Spatial Clustering of Applications with Noise,意即:一种基于密度,对噪声鲁棒的空间聚类算法。直观效果上看,DBSCAN算法可以找到样本点的全部密集区域,并把这些密集区域当做一个一个的聚类簇。

DBSCAN算法具有以下特点:

  • 基于密度,对远离密度核心的噪声点鲁棒

  • 无需知道聚类簇的数量

  • 可以发现任意形状的聚类簇

DBSCAN的算法步骤分成两步。

1,寻找核心点形成临时聚类簇。

2,合并临时聚类簇得到聚类簇。

完整教程可参考如下文章:

20分钟学会DBSCAN聚类算法

二,sklearn调包范例

1,生成样本点

%matplotlib inline
%config InlineBackend.figure_format = 'svg'
import numpy as np
import pandas as pd
from sklearn import datasets


X,_ = datasets.make_moons(500,noise = 0.1,random_state=1)
pdf = pd.DataFrame(X,columns = ['feature1','feature2'])

pdf.plot.scatter('feature1','feature2', s = 100,alpha = 0.6, title = 'dataset by make_moon');

2,调用dbscan方法完成聚类

%matplotlib inline
%config InlineBackend.figure_format = 'svg'

from sklearn.cluster import dbscan

# eps为邻域半径,min_samples为最少点数目
core_samples,cluster_ids = dbscan(X, eps = 0.2, min_samples=20) 
# cluster_ids中-1表示对应的点为噪声点

pdf = pd.DataFrame(np.c_[X,cluster_ids],columns = ['feature1','feature2','cluster_id'])
pdf['cluster_id'] = pdf['cluster_id'].astype('i2')

pdf.plot.scatter('feature1','feature2', s = 100,
    c = list(pdf['cluster_id']),cmap = 'rainbow',colorbar = False,
    alpha = 0.6,title = 'sklearn DBSCAN cluster result');

三,分布式实现思路

DBSCAN算法的分布式实现需要解决以下一些主要的问题。

1,如何计算样本点中两两之间的距离?

在单机环境下,计算样本点两两之间的距离比较简单,是一个双重遍历的过程。为了减少计算量,可以用空间索引如KDtree进行加速。

在分布式环境,样本点分布在不同的分区,难以在不同的分区之间直接进行双重遍历。为了解决这个问题,我的方案是将样本点不同的分区分成多个批次拉到Driver端, 然后依次广播到各个excutor分别计算距离,将最终结果union,从而间接实现双重遍历。

2,如何构造临时聚类簇?

这个问题不难,单机环境和分布式环境的实现差不多。

都是通过group的方式统计每个样本点周边邻域半径R内的样本点数量,

并记录它们的id,如果这些样本点数量超过minpoints则构造临时聚类簇,并维护核心点列表。

3,如何合并相连的临时聚类簇得到聚类簇?

这个是分布式实现中最最核心的步骤。

在单机环境下,标准做法是对每一个临时聚类簇,

判断其中的样本点是否在核心点列表,如果是,则将该样本点所在的临时聚类簇与当前临时聚类簇合并。并在核心点列表中删除该样本点。

重复此过程,直到当前临时聚类簇中所有的点都不在核心点列表。

在分布式环境下,临时聚类簇分布在不同的分区,无法直接扫描全局核心点列表进行临时聚类簇的合并。

我的方案是先在每一个分区内部对各个临时聚类簇进行合并,然后缩小分区数量重新分区,再在各个分区内部对每个临时聚类簇进行合并。

不断重复这个过程,最终将所有的临时聚类簇都划分到一个分区,完成对全部临时聚类簇的合并。

为了降低最后一个分区的存储压力,我采用了不同于标准的临时聚类簇的合并算法。

对每个临时聚类簇只关注其中的核心点id,而不关注非核心点id,以减少存储压力。合并时将有共同核心点id的临时聚类簇合并。

四,分布式实现核心逻辑

以下为DBSCAN的分布式实现的核心逻辑。即从临时聚类簇合并成聚类簇的方案,该逻辑较为精细,采用RDD来实现。

1,寻找核心点形成临时聚类簇。

假定已经得到了临时聚类簇,信息存储为rdd_core

#rdd_core的每一行代表一个临时聚类簇:(min_core_id, core_id_set)
#core_id_set为临时聚类簇所有核心点的编号,min_core_id为这些编号中取值最小的编号
rdd_core = sc.parallelize([(1,{1,2}),(2,{2,3,4}),(6,{6,8,9}),
        (4,{4,5}),(9,{9,10,11}),(15,{15,17}),(10,{10,11,18})],20)
data_core = rdd_core.collect()
data_core
[(1, {1, 2}),
 (2, {2, 3, 4}),
 (6, {6, 8, 9}),
 (4, {4, 5}),
 (9, {9, 10, 11}),
 (15, {15, 17}),
 (10, {10, 11, 18})]
#定义合并函数:将有共同核心点的临时聚类簇合并
def mergeSets(list_set):
    result = []
    while  len(list_set)>0 :
        cur_set = list_set.pop(0)
        intersect_idxs = [i for i in list(range(len(list_set)-1,-1,-1)) if cur_set&list_set[i]]
        while  intersect_idxs :
            for idx in intersect_idxs:
                cur_set = cur_set|list_set[idx]

            for idx in intersect_idxs:
                list_set.pop(idx)
                
            intersect_idxs = [i for i in list(range(len(list_set)-1,-1,-1)) if cur_set&list_set[i]]
        
        result = result+[cur_set]
    return result

# 测试mergeSets效果
test_list_set = [{1,2,3},{3,4,5},{10,12,13},{4,5,8},{13,15},{7,8},{20,22}]
print(mergeSets(test_list_set))

[{1, 2, 3, 4, 5, 7, 8}, {10, 12, 13, 15}, {20, 22}]
#对rdd_core分区后在每个分区合并,不断将分区数量减少,最终合并到一个分区
#如果数据规模十分大,难以合并到一个分区,也可以最终合并到多个分区,得到近似结果。
#rdd: (min_core_id,core_id_set)


def mergeRDD(rdd,partition_cnt):
    def fn(iterator):
        list_set = [x[1] for x in iterator]
        list_set_merged = mergeSets(list_set)
        merged_core = [(min(x),x) for x in list_set_merged] 
        return(iter(merged_core))
    rdd_merged = rdd.partitionBy(partition_cnt).mapPartitions(fn)
    return rdd_merged

rdd_core = sc.parallelize([(1,{1,2}),(2,{2,3,4}),(6,{6,8,9}),
        (4,{4,5}),(9,{9,10,11}),(15,{15,17}),(10,{10,11,18})],20)

#分区迭代计算,可以根据需要调整迭代次数和分区数量
rdd_core = mergeRDD(rdd_core,8)
rdd_core = mergeRDD(rdd_core,4)
rdd_core = mergeRDD(rdd_core,1)
rdd_core.collect() 
[(1, {1, 2, 3, 4, 5}), (6, {6, 8, 9, 10, 11, 18}), (15, {15, 17})]

五,分布式实现完整代码

1,生成样本点

%matplotlib inline
%config InlineBackend.figure_format = 'svg'

import numpy as np
import pandas as pd
from sklearn import datasets

#设置DBSCAN参数
eps = 0.2
min_samples=20

X,_ = datasets.make_moons(500,noise = 0.1,random_state=1)
pdf = pd.DataFrame(X,columns = ['feature1','feature2'])
pdf.plot.scatter('feature1','feature2', s = 100,alpha = 0.6, title = 'dataset by make_moon')

pdf.to_csv("./data/moon_dataset.csv",sep = "\t",index = False)

#转换成spark中的DataFrame
#dfdata = spark.createDataFrame(pdf)
dfdata = spark.read.option("header","true")\
  .option("inferSchema","true") \
  .option("delimiter", "\t") \
  .csv("data/moon_dataset.csv")

#将点的坐标生成一个array,并添加唯一id列

dfinput = spark.createDataFrame(dfdata.selectExpr("array(feature1,feature2) as point") \
    .rdd.map(lambda row:row["point"]).zipWithIndex()).toDF("point","id") \
    .selectExpr("id","point").persist(StorageLevel.MEMORY_AND_DISK)

dfinput.show() 
+---+--------------------+
| id|               point|
+---+--------------------+
|  0|[0.31655567612451...|
|  1|[0.74088269972429...|
|  2|[0.87172637133182...|
|  3|[0.55552787799773...|
|  4|[2.03872887867669...|
|  5|[1.99136342379999...|
|  6|[0.22384428620202...|
|  7|[0.97295674842244...|
|  8|[-0.9213036629723...|
|  9|[0.46670632489966...|
| 10|[0.49217803972132...|
| 11|[-0.4223529513452...|
| 12|[0.31358610070888...|
| 13|[0.64848081923216...|
| 14|[0.31549460745917...|
| 15|[-0.9118786633207...|
| 16|[1.70164131101163...|
| 17|[0.10851453318658...|
| 18|[-0.3098724480520...|
| 19|[-0.2040816479108...|
+---+--------------------+
only showing top 20 rows

2,分批次广播KDTree得到邻近关系

import numpy as np 
from pyspark.sql import types as T
from pyspark.sql import functions as F 
from pyspark.sql import Row,DataFrame
from sklearn.neighbors import KDTree


rdd_input = dfinput.rdd.repartition(20).persist(StorageLevel.MEMORY_AND_DISK)

#创建空dataframe
schema = T.StructType([
        T.StructField("m_id", T.LongType(), True),
        T.StructField("s_id", T.LongType(), True),
        T.StructField("m_point", T.ArrayType(T.DoubleType(),False), True),
        T.StructField("s_point", T.ArrayType(T.DoubleType(),False), True)])
 
dfpair_raw = spark.createDataFrame(spark.sparkContext.emptyRDD(), schema)


#分批次进行广播
partition_cnt = 10
dfmaster = dfinput.repartition(partition_cnt)

for i in range(0,partition_cnt):
    rdd_master_i = dfmaster.rdd.mapPartitionsWithIndex(
        lambda idx, iterator: iterator if (idx == i ) else iter([]) )
    master_i = rdd_master_i.collect()
    idxs_i = [x["id"] for x in master_i]
    points_i = [x["point"] for x in master_i]
    tree_i = KDTree(np.array(points_i), leaf_size=40, metric='minkowski') #构建kdtree
    broad_i = sc.broadcast((idxs_i,points_i,tree_i))
    
    def fn(iterator):
        list_res = [] #m_id,s_id,m_point,s_point
        idxs_i,points_i,tree_i = broad_i.value
        for row in iterator:
            s_id = row["id"]
            s_point = row["point"]
            index = tree_i.query_radius(np.array([s_point]), r=2*eps)[0] #根据kdtree查询一定范围内的点
            for j in index:
                list_res.append([idxs_i[j],s_id,points_i[j],s_point])
        return iter(list_res)
    
    dfpair_raw_i = spark.createDataFrame(rdd_input.mapPartitions(fn)).toDF("m_id","s_id","m_point","s_point")
    dfpair_raw = dfpair_raw.union(dfpair_raw_i)
    

3,根据DBSCAN邻域半径得到有效邻近关系 dfpair


# 根据DBSCAN邻域半径得到有效邻近关系 dfpair

spark.udf.register("distance", lambda p,q:((p[0]-q[0])**2+(p[1]-q[1])**2)**0.5)
dfpair = dfpair_raw.where("distance(s_point,m_point) <"+str(eps)) \
    .persist(StorageLevel.MEMORY_AND_DISK)

dfpair.show()
+----+----+--------------------+--------------------+
|m_id|s_id|             m_point|             s_point|
+----+----+--------------------+--------------------+
| 453| 190|[-0.9305763617157...|[-0.9342271003887...|
| 310| 190|[-0.8647371774956...|[-0.9342271003887...|
| 468| 191|[-1.1236573115927...|[-1.1107926480996...|
| 379| 193|[0.29384781266215...|[0.30668711048764...|
| 315| 193|[0.27137000939033...|[0.30668711048764...|
|  45| 193|[0.41589898081748...|[0.30668711048764...|
| 206| 194|[-0.0479181540769...|[-0.1280156580069...|
| 124| 194|[-0.0717324612290...|[-0.1280156580069...|
| 194| 194|[-0.1280156580069...|[-0.1280156580069...|
| 166| 194|[0.01894280256753...|[-0.1280156580069...|
| 348| 195|[0.77799441414636...|[0.91064797784686...|
| 104| 195|[0.97950311224508...|[0.91064797784686...|
| 243| 196|[-0.7132555992338...|[-0.7605955193004...|
| 482| 196|[-0.6342018198835...|[-0.7605955193004...|
| 174| 196|[-0.7070084501262...|[-0.7605955193004...|
| 196| 196|[-0.7605955193004...|[-0.7605955193004...|
| 367| 197|[0.00547311527928...|[0.02875754813338...|
| 437| 197|[0.04652353436126...|[0.02875754813338...|
| 237| 197|[0.08078546751286...|[0.02875754813338...|
| 226| 197|[0.07689272752608...|[0.02875754813338...|
+----+----+--------------------+--------------------+
only showing top 20 rows

4,创建临时聚类簇 dfcore

dfcore = dfpair.groupBy("s_id").agg(
  F.first("s_point").alias("s_point"),
  F.count("m_id").alias("neighbour_cnt"),
  F.collect_list("m_id").alias("neighbour_ids")
).where("neighbour_cnt>="+str(min_samples)) \
 .persist(StorageLevel.MEMORY_AND_DISK)

dfcore.show(3)
+----+--------------------+-------------+--------------------+
|s_id|             s_point|neighbour_cnt|       neighbour_ids|
+----+--------------------+-------------+--------------------+
|  26|[0.95199382446206...|           25|[150, 463, 300, 5...|
| 418|[0.04187413307127...|           22|[367, 454, 226, 4...|
|  65|[0.46872165251145...|           30|[45, 402, 44, 456...|
+----+--------------------+-------------+--------------------+
only showing top 3 rows

5,得到临时聚类簇的核心点信息

dfpair_join = dfcore.selectExpr("s_id").join(dfpair,["s_id"],"inner")
df_fids = dfcore.selectExpr("s_id as m_id")
dfpair_core = df_fids.join(dfpair_join,["m_id"],"inner")
rdd_core = dfpair_core.groupBy("s_id").agg(
  F.min("m_id").alias("min_core_id"),
  F.collect_set("m_id").alias("core_id_set")
).rdd.map(lambda row: (row["min_core_id"], set(row["core_id_set"])))

rdd_core.persist(StorageLevel.MEMORY_AND_DISK)

print("before_dbscan, rdd_core.count() = ",rdd_core.count())

6,对rdd_core分区分步合并

#定义合并函数:将有共同核心点的临时聚类簇合并
def mergeSets(list_set):
    result = []
    while  len(list_set)>0 :
        cur_set = list_set.pop(0)
        intersect_idxs = [i for i in list(range(len(list_set)-1,-1,-1)) if cur_set&list_set[i]]
        while  intersect_idxs :
            for idx in intersect_idxs:
                cur_set = cur_set|list_set[idx]

            for idx in intersect_idxs:
                list_set.pop(idx)
                
            intersect_idxs = [i for i in list(range(len(list_set)-1,-1,-1)) if cur_set&list_set[i]]
        
        result = result+[cur_set]
    return result

#对rdd_core分区后在每个分区合并,不断将分区数量减少,最终合并到一个分区
#如果数据规模十分大,难以合并到一个分区,也可以最终合并到多个分区,得到近似结果。
#rdd: (min_core_id,core_id_set)
def mergeRDD(rdd,partition_cnt):
    def fn(iterator):
        list_set = [x[1] for x in iterator]
        list_set_merged = mergeSets(list_set)
        merged_core = [(min(x),x) for x in list_set_merged] 
        return(iter(merged_core))
    rdd_merged = rdd.partitionBy(partition_cnt).mapPartitions(fn)
    return rdd_merged


#此处需要视实际情况调整分区数量和迭代次数
for pcnt in (16,8,4,1):
    rdd_core = mergeRDD(rdd_core,pcnt)
    
rdd_core.persist(StorageLevel.MEMORY_AND_DISK)

print("after dbscan: rdd_core.count()=",rdd_core.count())

after dbscan: rdd_core.count()= 2

7, 获取每一个core的簇信息

dfcluster_ids = spark.createDataFrame(
    rdd_core.flatMap(lambda t: [(t[0], s_id) for s_id in t[1]])).toDF("cluster_id","s_id")

dfclusters =  dfcore.join(dfcluster_ids, "s_id", "left")

dfclusters.show() 
+----+--------------------+-------------+--------------------+----------+
|s_id|             s_point|neighbour_cnt|       neighbour_ids|cluster_id|
+----+--------------------+-------------+--------------------+----------+
|  26|[0.95199382446206...|           25|[150, 463, 300, 5...|         2|
|  65|[0.46872165251145...|           30|[45, 402, 44, 456...|         0|
| 418|[0.04187413307127...|           22|[367, 454, 226, 4...|         0|
| 293|[0.74589456598500...|           30|[231, 293, 153, 3...|         2|
| 243|[-0.7132555992338...|           21|[243, 482, 174, 1...|         2|
| 278|[-0.8841688633151...|           27|[453, 310, 196, 9...|         2|
| 367|[0.00547311527928...|           24|[367, 437, 454, 2...|         0|
|  19|[-0.2040816479108...|           25|[206, 124, 194, 2...|         2|
|  54|[1.86506527195881...|           22|[331, 116, 92, 54...|         0|
| 296|[1.43490708002292...|           22|[212, 199, 473, 3...|         0|
|   0|[0.31655567612451...|           22|[315, 46, 456, 42...|         0|
| 348|[0.77799441414636...|           25|[348, 402, 374, 4...|         0|
| 415|[-0.4510104506178...|           28|[363, 407, 273, 2...|         2|
| 112|[1.38118745635267...|           28|[212, 199, 473, 3...|         0|
| 113|[1.95088315015933...|           26|[306, 255, 447, 2...|         0|
| 167|[0.39542492867803...|           22|[286, 179, 109, 1...|         2|
| 385|[-0.2769033877846...|           25|[363, 407, 122, 2...|         2|
| 237|[0.08078546751286...|           29|[367, 437, 46, 23...|         0|
| 347|[-0.7336250327143...|           21|[482, 174, 196, 9...|         2|
| 330|[0.71478678633618...|           27|[231, 293, 153, 3...|         2|
+----+--------------------+-------------+--------------------+----------+
only showing top 20 rows

8,求每一个簇的代表核心和簇元素数量

rdd_cluster = dfclusters.rdd.map(
    lambda row: (row["cluster_id"],(row["s_point"],row["neighbour_cnt"],set(row["neighbour_ids"])))
)

def reduce_fn(a,b):
    id_set = a[2]|b[2]
    result = (a[0],a[1],id_set) if a[1]>=b[1] else (b[0],b[1],id_set)
    return result

rdd_result = rdd_cluster.reduceByKey(reduce_fn)

def map_fn(t):
    cluster_id = t[0]
    representation_point = t[1][0]
    neighbour_points_cnt = t[1][1]
    id_set = list(t[1][2])
    cluster_points_cnt = len(id_set)
    return (cluster_id,representation_point,neighbour_points_cnt,cluster_points_cnt,id_set)

dfresult = spark.createDataFrame(rdd_result.map(map_fn)
    ).toDF("cluster_id","representation_point","neighbour_points_cnt","cluster_points_cnt","cluster_points_ids")

dfresult.persist(StorageLevel.MEMORY_AND_DISK)

dfresult.show(3)

+----------+--------------------+--------------------+------------------+--------------------+
|cluster_id|representation_point|neighbour_points_cnt|cluster_points_cnt|  cluster_points_ids|
+----------+--------------------+--------------------+------------------+--------------------+
|         0|[1.95163238902570...|                  32|               242|[0, 1, 4, 5, 6, 1...|
|         2|[0.95067226301300...|                  34|               241|[2, 3, 7, 9, 11, ...|
+----------+--------------------+--------------------+------------------+--------------------+

注意到我们的结果中

聚类簇数量为2个。

噪声点数量为500-242-241 = 17个

和调用sklearn中的结果完全一致。

9,求每一个点的簇id,噪声点簇id赋值为-1

rdd_clusterid = dfresult.select("cluster_id","cluster_points_ids").rdd.flatMap(
    lambda t: [(x,t["cluster_id"]) for x in t["cluster_points_ids"]])

df_clusterid = spark.createDataFrame(rdd_clusterid).toDF("id","cluster_id")
dfoutput_raw = dfinput.join(df_clusterid,"id","left")
dfoutput = dfoutput_raw.na.fill(-1)

dfoutput = dfoutput.selectExpr("id","cluster_id","point[0] as feature1","point[1] as feature2")
dfoutput.persist(StorageLevel.MEMORY_AND_DISK)
dfoutput.show()

+---+----------+--------------------+--------------------+
| id|cluster_id|            feature1|            feature2|
+---+----------+--------------------+--------------------+
| 26|         2|  0.9519938244620684|  0.2552474492493959|
| 29|         2|  -0.863622604833635|   0.756640145262391|
|474|         2| -0.4885096982719171|  1.0491748634589007|
| 65|         0|  0.4687216525114598| -0.3609345154042032|
|191|         2|  -1.110792648099675| 0.18780773522847397|
|418|         0| 0.04187413307127004| 0.25141384401180206|
|222|         0|  1.5899557693512685|-0.42942807171107017|
|270|         0|   2.178538623657351| 0.44807664826862253|
|293|         2|  0.7458945659850041|  0.5914004203001728|
|243|         2| -0.7132555992338488|  0.8089869542594612|
|278|         2| -0.8841688633151701|  0.5147890731484406|
|367|         0|0.005473115279280807| 0.12361319219864111|
|442|         2|  0.8889028924942911|0.028497180983055058|
| 19|         2| -0.2040816479108034|  0.9856890760075208|
| 54|         0|  1.8650652719588168|-0.13541631999968182|
|296|         0|   1.434907080022921| -0.4713434821495917|
|  0|         0|  0.3165556761245117|-0.04942181785843226|
|277|         0| 0.08946739589070024|-0.22831869307482952|
|287|         0|  1.7814103104861185|-0.33109829058582907|
|348|         0|   0.777994414146364| -0.4522352978300379|
+---+----------+--------------------+--------------------+
only showing top 20 rows

10,保存和可视化结果

dfoutput.write.format("csv") \
  .option("header","true")\
  .option("inferSchema","true") \
  .option("delimiter", "\t") \
  .save("data/dbscan_output.csv")
pdfoutput = dfoutput.toPandas()

pdfoutput.plot.scatter('feature1','feature2', s = 100,
    c = list(pdfoutput['cluster_id']),cmap = 'rainbow',colorbar = False,
    alpha = 0.6,title = 'pyspark DBSCAN cluster result');

如果本书对你有所帮助,想鼓励一下作者,记得给本项目加一颗星星star⭐️,并分享给你的朋友们喔????!

  公众号后台回复关键词:pyspark,获取本项目github地址。

SparkSQL 执行底层原理解析 从Spark SQL 底层架构可以看到,我们写的SQL语句,经过一个优化器(Catalyst),转化为RDD,交给集群执行。SQL到RDD中间经过了一个Catalyst,它就是Spark SQL的核心,是针对Spark SQL语句执行过程中的查询优化框架,基于Scala函数式编程结构。 阅读详情

相关推荐

SparkSQL知识点总结

零基础学sparksql

默主归沙的博客 4194

SparkSQL和rdd的使用详解

Spark SQL是 Apache Spark 用于处理结构化数据(DataFrame和Datasets)的模块。

qq_71257020的博客 1478

SparkSQL中 RDD,DF和DS之间的相互转换

类型之间的转换总结 RDD、DataFrame、Dataset 三者有许多共性,有各自适用的场景常常需要在三者之间转换DataFrame/Dataset 转RDD: 这个转换很简单 val rdd1=testDF.rdd val rdd2=testDS.rdd RDD 转DataFrame: import spark.implicits._ val testDF = rdd.map {l...

muyingmiao的专栏 2766

SparkSQL与RDD

1.RDD是Spark的核心抽象,全称为Resillient Distributed Dataset,即弹性分布式数据集2.RDD在抽象上来说是一种元素集合,包含了数据。它是被分区的,分为多个分区,每个分区分布3.在集群中的不同节点上(分区即partition),从而让RDD中的数据可以被并行操作。(分布式的特性)4.RDD通常通过Hadoop上的文件,即HDFS文件,来进行创建;有时也可以通过Spark应用程序中的集合来创建。

2401_83215220的博客 1174

WebDriverAgent 菜鸟解析篇

WebDriverAgent 是一个用于模拟移动设备浏览器的 Python 库,可以用来在 PC 上模拟移动设备的浏览器操作,从而进行自动化测试。下面是一个菜鸟对 WebDriverAgent 源码的解析篇,希望能对初学者有所帮助。

DJ355的博客 1223

统信UOS安装CH384串口卡驱动

本文讲解如何在统信UOS系统下安装CH384 PCIE多串口驱动安装过程,CPU为飞腾D2000。其他Linux内核系统安装亦可参照类似方法。

shjhuang的专栏 1887

SparkSQL详解

Spark SQL是 Spark 用来处理结构化数据的一个模块,它提供了 2 个编程抽象:DataFrame 和 DataSet,并且作为分布式 SQL 查询引擎的作用。

qq_40322236的博客 2842

Langchain与Milvus集成中的pymilvus.exceptions.MilvusException: channel not found错误解析

本文深入解析了在Langchain与Milvus集成时常见的pymilvus.exceptions.MilvusException: channel not found错误。该错误通常由客户端配置错误、网络问题、服务端状态异常或版本不兼容导致。文章提供了从基础连通性测试到高级网络排查的完整故障排除指南,并针对本地Docker、远程服务器及Kubernetes等典型部署场景给出了具体解决方案,帮助开发者快速定位并修复这一连接层问题。

weixin_29229989的博客 217

Spark学习

Spark是大数据处理中的一个非常重要的组件,一般使用Hadoop在底层作为分布式存储系统,上层使用Spark代替Hadoop原来的MapReduce此外还提供RDD编程、Spark SQL、流计算和机器学习。它功能齐全、计算速度快,可以使用Scala语言、python、Java进行编程,那么在本周学习了Spark的相关内容,目录如下: 一、Spark的设计与运行原理 Spark概述 Spa...

weixin_44961794的博客 243

基于ADMM的TV正则化最小化稀疏实现图像重建

ADMM算法是一种求解带有约束条件的优化问题的有效方法,它可以将原问题分解成两个子问题,然后交替求解这两个子问题,直到收敛。为了解决这些问题,研究人员提出了许多图像重建方法,其中一种常用的方法是基于正则化的方法。在图像重建中,TV正则化是一种比较常用的正则化方法,可以有效地降低图像的噪声,并保持图像细节特征。通过本文的介绍和源代码的演示,我们可以看出基于ADMM的TV正则化最小化稀疏实现图像重建的方法是一种简单、高效、有效的图像重建方法,能够在一定程度上提高图像质量,对于实际应用具有一定的参考意义。

学习使你进步。 1762

Python之abbrobotegm包语法、参数和实际应用案例

`abbrobotegm` 是用于控制 ABB 机器人的 Python 接口包,主要通过 EGM(Externally Guided Motion,外部引导运动)协议与 ABB 机器人控制器进行通信,实现对机器人的实时位置控制、状态监控和运动引导等功能。该包适用于需要将 ABB 机器人与外部系统(如视觉系统、传感器等)集成的场景。

王国平的博客 1255

SparkSQL和RDD,DataFrame,DataSet的转换

1.简介: ​ SparkSQL的前身是Shark,Shark的底层实现是依赖于Hive,Shark的发展受制于Hive的发展,后来项目组将Shark项目废弃,保留了其中的一些非常优秀的特点:比如内存列存储技术,动态字节码技术等等,重新组织了一个项目,这个项目就是SparkSQL,同时在hive里面也推出了一个子模块,是hive的计算引擎基于spark,hive-on-spark ​ 需要明确...

weixin_44142672的博客 3000

基于模型上下文协议(MCP)的可插拔式临床AI工具链Clinical DS研究(下)

本研究深入探讨了医疗AI在临床落地中的核心矛盾,即如何从“模型能答”迈向“系统可信、可用、可追溯”。为解决此矛盾,我们创新性地提出并详细设计实现了一套基于模型上下文协议(MCP)的**“可插拔式临床AI工具链”**架构。通过将复杂的医疗AI系统解构为Host、MCP Server和标准协议三层,并将各类能力(临床决策、影像分析、合规审计)封装为标准的、可独立演进的Server,我们成功地构建了一个模块化、高内聚、低耦合的系统。

AllenLV的博客 1382

SparkSql---RDD DataFrame DataSet

DataSet 是 Spark 1.6 中添加的一个新抽象,是 DataFrame的一个扩展。它提供了 RDD 的优势(强类型,使用强大的 lambda 函数的能力)以及 SparkSQL 优化执行引擎的优点。DataFrame 与 RDD 的主要区别在于,RDD只关心数据,而DataFrame中也包含数据描述信息(数据的元数据)。DataFrame 其实就是 DataSet 的一个特例 ,因为DataFrame中每行的类型都是Row。定义一个名为user的json文件,文件中的数据如下。

weixin_47109902的博客 2046

【WRF-Chem工具】anthro_emiss 详解:生成人为排放数据

anthro_emiss 详解:生成人为排放数据

WW、forever的博客 2056

42、使用mmrotate中k3det进行旋转目标检测,并进行mnn部署和ncnn部署

基本思想:仍然是身份证分割,并进行ncnn和mnn的c++的部署开发

sxj731533730 5713

spark中Rdd依赖和SparkSQL介绍--学习笔记

rdd依赖,spark运行流程,sparkShuffle过程,Spark并行度设置,spark调优,SparkSQL基础学习

yfq_29的博客 1392

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

配置sparksql读hive,dataframe和RDD,将RDD转换成Dataframe,视图,withcolumn

退出spark-shell :quit 使用spark自带文件 [hadoop@hadoop000 resources]$ pwd /home/hadoop/app/spark-2.4.0-bin-2.6.0-cdh5.7.0/examples/src/main/resources scala&amp;amp;amp;amp;amp;amp;gt; val df = spark.read.json(&amp;amp;amp;amp;amp;quot;file:///hom

qq_36459386的博客 2546

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

大数据技术之SparkSQL(超级详细)

Spark SQL是Spark用来处理结构化数据的一个模块,它提供了2个编程抽象:DataFrame和DataSet,并且作为分布式SQL查询引擎的作用。 它是将Hive SQL转换成MapReduce然后提交到集群上执行,大大简化了编写MapReduc的程序的复杂性,由于MapReduce这种计算模型执行效率比较慢。所以Spark SQL的应运而生,它是将Spark SQL转换成RDD,然后提交到集群执行,执行效率非常快!

专注 AI、大数据、Python、爬虫、数据分析可视化、机器学习、推荐算法、知识图谱、大模型应用与全栈系统开发,长期沉淀真实项目案例、源码解析、系统设计、部署经验与工程化实践,分享可运行、可复用、可落地的技术方案。 3万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

SparkSQL

大数据自学笔记——spark学习笔记

我的笔记 1万+

SparkSQL之RDD转换DataFrame

文章目录1 、SparkSQL读取Json文件2、RDD转换成DataFrame2.1 用toDF()方式2.2 把原生RDD转换成RDD[Row],再和定义好的StructType匹配 1 、SparkSQL读取Json文件 先随便造两份Json格式数据。 [hadoop@vm01 data]$ vi stu1.json {"id":"1","name":"zhangsan","phone":...

11号车厢 654

SparkSQL中的RDD,DF和DS的转换

package com.zyc.sparksql import java.text.SimpleDateFormat import java.util.Locale import org.apache.spark.rdd.RDD import org.apache.spark.sql.{DataFrame, SparkSession} /** * Created with IntelliJ IDEA. * Author: zyc2913@163.com * Date: 2020/10/10 13:55

zyc2913的博客 1159

Spark之【SparkSQL编程】系列(No3)——《RDD、DataFrame、DataSet三者的共性和区别》

        本篇作为【SparkSQL编程】系列的第三篇博客,为大家介绍的是RDD、DataFrame、DataSet三者的共性和区别。         码字不易,先赞后看,养成习惯! 文章目录5. RDD、DataFrame、Dat...

大数据梦想家 1万+

SparkSQL 中 DataFrame 的转换

在大数据处理领域,SparkSQL 凭借其强大的数据处理能力和高效的计算性能备受青睐。其中,DataFrame 作为重要的数据结构,掌握它与其他数据类型(如 RDD、DataSet)之间的转换操作至关重要。今天,我们就深入探讨在 Spark 中如何进行 DataFrame 的转换,帮助大家更好地驾驭 SparkSQL 处理各类数据场景。

天冬忘忧的博客 1698

《SparkSQL剖析》SparkSQL到RDD概述

SparkSQL 到RDD val spark = SparkSession . builder(). appName("example").master("local").getOrCreate() spark.read.json("student.json").createOrReplaceTempView("student") spark.sql("select name from stud...

SW_LCC的博客 704

[Spark]直接调用RDD的方式实现SparkSQL的Filter操作

使用SQL实现数据过滤 import org.apache.spark.sql.SparkSession object SqlExample { def main(args: Array[String]): Unit = { val spark = SparkSession .builder() .appName("Spark sql whole stage ...

I have a adream 1782
上一篇: 7道SparkSQL编程练习题
下一篇: 探索MLlib机器学习
算法美食屋
博客等级 码龄9年 2万+粉丝 154原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值