Introducción a los conceptos básicos de PySpark (2): RDD y sus operadores comunes

Mejor experiencia de lectura: Introducción a los conceptos básicos de PySpark (2): RDD y sus operadores comunes - Nuggets (juejin.cn)

Tabla de contenido

Introducción a los RDD

Codificación RDD

Introducción a los RDD

RDD (Conjunto de datos distribuido resistente) es un conjunto de datos distribuido resistente , la abstracción de datos más básica en Spark, que representa una colección inmutable y particionable de elementos que se pueden calcular en paralelo

Conjunto de datos: un conjunto de datos para almacenar datos.

Distribuido: los datos en RDD se almacenan de manera distribuida y se pueden usar para computación distribuida.

Resiliente: los datos en RDD se pueden almacenar en la memoria o en el disco

Características de los RDD

  1. RDD está particionado; la partición RDD es la unidad más pequeña de almacenamiento de datos
  2. El método de RDD actuará en todas sus particiones.
  3. Existe una relación de dependencia entre los RDD (relación de sangre)
  4. RDD de tipo Key-Value puede tener un particionador (tipo Key-Value, lo que significa que los datos almacenados en RDD son una tupla)
  5. El plan de partición RDD estará lo más cerca posible del particionador donde se encuentran los datos (es decir, el Exector y el bloque que almacena los datos deben estar en el mismo servidor tanto como sea posible, de modo que solo se requiera la lectura del disco y no Se requiere E/S de red, pero debe garantizar capacidades informáticas paralelas A continuación, realice la lectura local)

Codificación RDD

SparkContext

El objeto SparkContext es el objeto de entrada del programa para la programación RDD

Esencialmente, la función principal de SparkContext para la programación es crear el primer RDD

conf = SparkConf().setAppName("test").setMaster("local[*]")
# 构建SparkContext对象
sc = SparkContext(conf=conf)

Acerca de la configuración de conf:

1. setMaster()Si se ejecuta localmente, complete local, si se envía a yarn, complete"yarn"

2. Configure los archivos dependientes:conf.set("spark.submit.pyFiles", 文件)

El formato del archivo puede ser .py o .zip

Al enviar en Linux, --py-files .zip/.pyenvíe los archivos dependientes por

Cómo se crean los RDD

Paralelizar la creación (paralelizar)

método:sc.parallelize(集合对象,分区数)

rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9])
print("默认分区数: ", rdd.getNumPartitions())

rdd = sc.parallelize([1, 2, 3], 3)
print("分区数: ", rdd.getNumPartitions())

getNumPartitions: Obtenga el número de particiones del rdd actual

Leer creación de archivo (textFile )

método:sc.textFile(文件路径,最小分区数)

La ruta del archivo aquí puede ser una ruta relativa o una ruta absoluta, se pueden leer datos locales o datos hdfs;

Tenga en cuenta que la lectura de archivos de Windows localmente y la conexión SSH a las rutas de archivos de lectura de Linux son diferentes

Windows puede escribir directamente la ruta del archivo localmente, y la ruta en Linux es file://+/home/wuhaoyi/......

# 读取本地文件
sc.textFile("../data/input/words.txt")

# 设置最小分区数
sc.textFile("../data/input/words.txt", 3)

# 注意,设置的最小分区数太大时会失效 
sc.textFile("../data/input/words.txt", 100) # 设置100分区不会生效

# 读取hdfs文件
sc.textFile("hdfs://10.245.150.47:8020/user/wuhaoyi/input/words.txt")

Si no se establece el número mínimo de particiones, se utilizará el valor predeterminado. El número predeterminado de particiones no tiene nada que ver con la CPU. Si es un archivo local, está relacionado con el tamaño del archivo; si es un archivo en HDFS, está relacionado con el número de bloques.


método:sc.wholeTextFiles(文件路径,最小分区数)

Función: leer un montón de archivos pequeños

rdd= sc.wholeTextFiles("../data/input/tiny_files")

Su uso textFilees básicamente el mismo que el método. Cabe señalar que esta API se utiliza para leer archivos pequeños, y también puede usar carpetas como parámetros para leer todos los archivos pequeños en la carpeta, por lo tanto, demasiadas particiones causarán muchos Shuffle innecesarios. , por lo que debe leer datos en la menor cantidad de particiones posible; la cantidad máxima de particiones se puede configurar para que sea la misma que la cantidad de archivos;

rdd= sc.wholeTextFiles("../data/input/tiny_files/",6)
print("当前分区数为",rdd.getNumPartitions())

Dado que se están leyendo 5 archivos, establecer la partición en 6 no es válido:

El formato rdd creado a través de esta API es el siguiente:


operador RDD

Operador: API en objetos de colección distribuidos

Los operadores RDD se dividen en Transformación (operador de conversión) y Acción (operador de acción);

Operador de transformación:

El valor devuelto sigue siendo un objeto RDD;

Características: el operador tiene carga diferida, si no hay un operador de acción, el operador de transformación no funcionará

Operador de acción:

El valor devuelto ya no es un objeto RDD

El operador de transformación es equivalente a construir un plan de ejecución, y el operador de acción hace que el plan de ejecución funcione.


 Operadores de transformación de uso común

mapa

Función: procesa los datos en RDD uno por uno de acuerdo con la lógica de función definida

Ejemplo:

rdd.map(lambda x:x*10)

mapa plano

Función: realizar operaciones de mapa en objetos RDD y anidamiento

Significado de anidar:

Ejemplo:

rdd = sc.parallelize(["hadoop spark hadoop", "spark hadoop hadoop", "hadoop flink spark"])
rdd2 = rdd.flatMap(lambda line: line.split(" "))
print(rdd2.collect())

resultado de la operación:

reducirPorClave

Función: para RDD tipo KV, agrupe automáticamente por clave y luego complete la agregación de datos (valor) en el grupo de acuerdo con la lógica de agregación

Ejemplo:

rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 1), ('a', 1)])
# 以key为单位进行计数
print(rdd.reduceByKey(lambda a, b: a + b).collect())

resultado:

Cabe señalar que reduceByKey solo es responsable de las operaciones de agregación, no de agrupación, que se completa automáticamente de acuerdo con la clave;

agrupar por

Función: Agrupa los datos de RDD

Sintaxis: rdd.groupBy(func); donde func es una función lógica de agrupación

Ejemplo:

rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 2), ('b', 3)])
# 根据key进行分组
# t代表的是每一个元组,t[0]代表的就是key
result = rdd.groupBy(lambda t: t[0])
print(result.collect())

resultado:

filtrar

Función: filtrar los datos en RDD

Sintaxis: rdd.filter(func); El valor de retorno de func debe ser verdadero o falso

Ejemplo:

rdd = sc.parallelize([1, 2, 3, 4, 5, 6])
# 通过Filter算子, 过滤奇数
result = rdd.filter(lambda x: x % 2 == 1)
print(result.collect())

resultado:

distinto

Función: deduplicar datos en RDD

Sintaxis: rdd.distinct(去重分区数量)(los parámetros generalmente se dejan en blanco)

Ejemplo:

rdd = sc.parallelize([1, 1, 1, 2, 2, 2, 3, 3, 3])
print(rdd.distinct().collect())

resultado:

Unión

Función: Combinar dos rdds en 1 rdd

gramática:rdd.union(other_rdd)

Ejemplo:

rdd1 = sc.parallelize([1, 1, 3, 3])
rdd2 = sc.parallelize(["a", "b", "a",(1,23),[1,2]])
rdd3 = rdd1.union(rdd2)
print(rdd3.collect())

resultado:

También se puede ver a partir de esto que también se pueden mezclar diferentes tipos de rdd;

unirse

Función: implementar la operación JOIN en dos rdds (similar a la conexión con SQL, también puede realizar la unión externa izquierda/derecha)

La operación de unión solo puede funcionar con grupos binarios

Sintaxis : rdd.join(other_rdd);;rdd.leftOuterJoin(other_rdd)rdd.rightOuterJoin(other_rdd)

Ejemplo:

	rdd1 = sc.parallelize([ (1001, "zhangsan"), (1002, "lisi"), (1003, "wangwu"), (1004, "zhaoliu") ])
    rdd2 = sc.parallelize([ (1001, "销售部"), (1002, "科技部")])
	# 连接
    print(rdd1.join(rdd2).collect())
    # 左外连接
    print(rdd1.leftOuterJoin(rdd2).collect())

resultado:

intersección

Función: encontrar la intersección de dos rdd

gramática:rdd.intersection(other_rdd)

Ejemplo:

    rdd1 = sc.parallelize([('a', 1), ('a', 3)])
    rdd2 = sc.parallelize([('a', 1), ('b', 3)])
    # 求交集
    rdd3 = rdd1.intersection(rdd2)
    print(rdd3.collect())

resultado:

brillo

Función: Anidar los datos de rdd, el anidamiento se realiza según la partición

gramática:rdd.glom()

Ejemplo:

    rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)
    print(rdd.glom().collect())

resultado:

Si desea anidar, simplemente use flatMap:

    rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)
    print(rdd.glom().flatMap(lambda x:x).collect())

resultado:

grupoPorClave

Función: para KV tipo rdd, grupo por tecla

Sin operaciones agregadas en comparación con reduceByKey

gramática:rdd.groupByKey(func)

Ejemplo:

    rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 1), ('b', 1)])
    rdd2 = rdd.groupByKey()
    print(rdd2.map(lambda x: (x[0], list(x[1]))).collect())

resultado:

Ordenar por

Función: ordenar los datos en rdd

gramática:rdd.sortBy(func,ascending=False,numPartitions=1)

ascendente: verdadero para orden ascendente, falso para orden descendente

numPartitions: cuántas particiones se utilizan para la clasificación; si se desea una ordenación global, el número de particiones de clasificación debe establecerse en 1

Ejemplo:

rdd = sc.parallelize([('c', 3), ('f', 1), ('b', 11), ('c', 3), ('a', 1), ('c', 5), ('e', 1), ('n', 9), ('a', 1)], 3)
# 按照key来进行全局降序排序
print(rdd.sortBy(lambda x: x[0], ascending=False, numPartitions=1).collect())

resultado:

ordenar por clave

Función: Para KV tipo rdd, ordenar según clave

gramática:rdd.sortByKey(ascending=False,numPartitions=1,keyfunc=<function RDD.lambda>)

keyfunc es una operación de preprocesamiento en la clave antes de ordenar, y el resto de los parámetros son los mismos que sortBy

Ejemplo:

    rdd = sc.parallelize([('a', 1), ('E', 1), ('C', 1), ('D', 1), ('b', 1), ('g', 1), ('f', 1),
                          ('y', 1), ('u', 1), ('i', 1), ('o', 1), ('p', 1),
                          ('m', 1), ('n', 1), ('j', 1), ('k', 1), ('l', 1)], 3)
	# 先将key全部转化为小写字母,然后根据key值进行全局升序排序
    print(rdd.sortByKey(ascending=True, numPartitions=1, keyfunc=lambda key: str(key).lower()).collect())

resultado:

Cabe señalar aquí que en el rdd ordenado, el valor de la clave no cambiará debido al preprocesamiento

mapValues

Función: Para el grupo binario RDD, realiza la operación de mapa en sus valores;

gramática:rdd.mapValues(func)

Ejemplo:

Por supuesto, también se puede implementar con mapa;

Operadores de acción de uso común

contar por clave

Función: Cuente el número de ocurrencias clave, generalmente utilizado para KV tipo rdd

gramática:rdd.countByKey()

recolectar

Función: recopile los datos de cada partición de rdd en el controlador para formar un objeto de lista

gramática:rdd.collect()

reducir

Función: agregar los datos en rdd según la lógica de entrada

gramática:rdd.reduce(func)

doblar

Función: similar a reduce, agrega los datos en rdd, pero la agregación tiene un valor inicial

gramática:rdd.fold(value,func)

Nota: cuando se agrega cada partición, se requiere el valor inicial; cuando se agregan las particiones, también se requiere el valor inicial;

Ejemplo:

rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9], 3)
print(rdd.fold(10, lambda a, b: a + b))

Resultado: 85

El valor inicial es 10, la agregación dentro de las tres particiones debe ser +10 y la agregación entre particiones es entonces +10

primero

Función: Sacar el primer elemento de rdd

gramática:rdd.first()

llevar

Función: Saca los primeros N elementos de rdd, forma una lista y regresa

gramática:rdd.take(N)

arriba

Función: Ordena los datos en rdd en orden descendente, saca la primera N para formar una lista y regresa

gramática:rdd.top(N)

Nota: la clasificación de la parte superior no se ve afectada por las particiones;

Ejemplo:

sc.parallelize([1,2,3,4,5,6,7],2).top(5)

resultado:

contar

Función: cuenta cuántos datos hay en rdd

gramática:rdd.count()

tomarmuestra

Función: muestras aleatorias de datos de rdd

gramática:rdd.takeSample(是否重复,采样数,随机数种子)

Ya sea para repetir: True permite la repetición, False no permite la repetición; la repetición aquí se refiere a tomar los datos en la misma posición, lo que no tiene nada que ver con el contenido de los datos

Número de muestras: cuántos datos se toman en total

Semilla de número aleatorio: cualquier número es suficiente; este parámetro generalmente no se completa, si se completa el mismo número en dos muestreos, los resultados de los dos muestreos son los mismos

Ejemplo:

rdd = sc.parallelize([1, 3, 5, 3, 1, 3, 2, 6, 7, 8, 6], 1)
print(rdd.takeSample(False, 5, 1))

resultado:

Ejecútelo dos veces y encuentre que las muestras son idénticas

tomarOrdenado

Función: ordena RDD, toma la primera N

gramática:rdd.takeOrdered(N,func)

A través de la expresión lambda en func, puede lograr operaciones ascendentes/descendentes; en comparación con top, no solo puede ordenar en orden ascendente

El valor predeterminado es el orden ascendente

Ejemplo:

    rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 1)
	# 升序
    print(rdd.takeOrdered(3)) 
	# 降序
    print(rdd.takeOrdered(3, lambda x: -x))

resultado:

Se puede ver que la función en la expresión lambda procesa los datos antes de ordenarlos, pero no afecta los datos originales.

para cada

Función: realiza operaciones lógicas de entrada en los datos en rdd (similar al mapa, excepto que no hay valor de retorno)

gramática:rdd.foreach(func)

Ejemplo:

    rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 1)
    result = rdd.foreach(lambda x: print(x * 10))

resultado:

Imprima la salida directamente por los ejecutores sin pasar por el controlador

guardar como archivo de texto

Función: escribir datos rdd en un archivo de texto (local o hdfs)

gramática:rdd.saveAsTextFile(filePath)

Ejemplo:

    rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 3)
    rdd.saveAsTextFile("hdfs://10.245.150.47:8020/user/wuhaoyi/output/out1")

resultado:

Al guardar archivos, se ejecuta de manera distribuida sin pasar por el controlador, por lo que cada partición generará un archivo guardado;

Tenga en cuenta que los archivos guardados y sus archivos de verificación se almacenan en una carpeta, y esta carpeta no se puede crear por adelantado ;

operador de partición

mapPartitions(transformación)

Función: operación de mapa en unidades de particiones

gramática:rdd.mapPartitions(func)

Ejemplo:

    rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 3)

    def process(iter):
        result = []
        for it in iter:
            result.append(it * 10)

        return result


    print(rdd.mapPartitions(process).collect())

resultado:

foreachPartition (acción)

Función: similar a foreach, procesa los datos de una partición completa a la vez;

gramática:rdd.foreachPartition(func)

Ejemplo:

Nota: No hay un orden de salida fijo para este método, la partición que se procese primero será la primera salida.

partición por (transformación)

Función: realizar una operación de partición personalizada en RDD

gramática:rdd.partitionBy(partitionNum,func)

Parámetro 1: Número de particiones personalizadas; Parámetro 2: Reglas de partición personalizadas

Ejemplo:

    rdd = sc.parallelize([('hadoop', 1), ('spark', 1), ('hello', 1), ('flink', 1), ('hadoop', 1), ('spark', 1)])

    # 使用partitionBy 自定义 分区
    def process(k):
        if 'hadoop' == k or 'hello' == k: return 0
        if 'spark' == k: return 1
        return 2


    print(rdd.partitionBy(3, process).glom().collect())

resultado:

reparación/aglutinación

Función: volver a particionar los datos en RDD

Sintaxis: rdd.reparation(N)/rdd.coalesce(N,是否允许shuffle)

En coalesce, el parámetro 2 indica si se permite la partición, True significa que se permite la mezcla y se pueden agregar particiones, y False significa que no se permite la mezcla y no se pueden agregar particiones;

En Spark, excepto que la clasificación global debe establecerse en 1 partición, las API relacionadas con la partición generalmente se ignoran en otros casos.

Por lo tanto, no se recomienda volver a particionar mediante reparación; afectará el cómputo paralelo y, si la partición aumenta, también puede causar barajar

Ejemplo:

    rdd = sc.parallelize([1, 2, 3, 4, 5], 3)

    # repartition 修改分区
    print(rdd.repartition(1).getNumPartitions())

    print(rdd.repartition(5).getNumPartitions())

    # coalesce 修改分区(这里因为shuffle为False,所以不能增加分区,但可以减少分区)
    print(rdd.coalesce(1).getNumPartitions())

    print(rdd.coalesce(5, shuffle=True).getNumPartitions())

resultado:

En comparación con la reparación, generalmente se recomienda usar coalesce

Supongo que te gusta

Origin blog.csdn.net/qq_51235856/article/details/130464926
Recomendado
Clasificación