pyspark常用方法总结

一、介绍

1、Hadoop介绍

Hadoop基本概念

Hadoop是由Apache基金会开发的分布式系统基础架构,主要用于解决海量数据的存储和分析计算问题。它允许开发者在无需了解分布式系统底层细节的情况下,开发分布式程序并充分利用集群的计算和存储能力

Hadoop的三大核心组件

  • HDFS‌:分布式文件系统,负责海量数据存储。
  • YARN‌:资源调度系统,负责集群资源管理和任务调度。
  • MapReduce‌:分布式计算框架,负责海量数据计算。

架构特点

  • 高容错性‌:设计用于部署在低成本硬件上
  • 高吞吐量‌:适合处理超大数据集
  • 流式数据访问‌:放宽POSIX要求,优化大数据处理

Hadoop生态圈包含众多技术组件

  • 存储层‌:HBase(列式数据库)、HDFS
  • 计算层‌:MapReduce、Spark
  • 数据仓库‌:Hive、Pig
  • 数据采集‌:Flume、Sqoop
  • 协调服务‌:Zookeeper
  • 机器学习‌:Mahout

典型应用场景

  • 大规模日志分析
  • 推荐系统数据计算
  • 数据仓库构建
  • 机器学习训练数据准备

2、Spark介绍

Spark是Apache基金会下的开源分布式计算系统,Spark是一个基于内存的分布式计算框架,专为大规模数据处理而设计,具有快速、通用和易用的特点。

核心特性

  • 速度优势‌:比Hadoop MapReduce快10-100倍,主要得益于内存计算和优化的执行引擎。
  • 易用性‌:支持Java、Scala、Python和R等多种编程语言。
  • 通用性‌:提供SQL、流处理、机器学习和图计算等统一API。
  • 容错性‌:通过RDD的血缘关系(lineage)实现数据恢复。
  • 灵活性‌:可在Hadoop YARN、Mesos、Kubernetes或独立集群上运行。

核心数据结构

RDD(弹性分布式数据集)是Spark最基础的数据抽象,代表一个不可变的、可分区的分布式数据集合它具有以下核心特性:

  • 弹性特征‌(自动从故障中恢复)):

    • 存储弹性:内存与磁盘自动切换
    • 容错弹性:数据丢失可自动恢复
    • 计算弹性:出错重试机制
    • 分片弹性:可根据需要重新分片
  • 分布式特性‌:

    • 数据分布在集群不同节点上
    • 支持并行计算
  • 编程模型‌:

    • 提供转换(Transformations)和动作(Actions)两类操作
    • 转换操作是惰性的,只有遇到动作操作才会执行

DataFrame是Spark 1.3引入的以RDD为基础的分布式数据集类似于传统数据库中的二维表格。主要特点包括:

  1. 结构化特性‌:

    • 带有schema元信息(列名和类型)
    • 每行类型固定为Row对象
  2. 性能优化‌:

    • 支持off-heap存储,减少GC开销
    • 支持执行计划优化(Catalyst优化器)
  3. API优势‌:

    • 提供高层关系操作,比RDD API更友好
    • 支持SQL查询接口

DataFrame与RDD对比‌:

  • RDD不了解数据结构,DataFrame有详细schema信息
  • RDD是分布式Java对象集合,DataFrame是分布式Row对象集合

Dataset是Spark 1.6引入的API,结合了RDD和DataFrame的优点。核心特性包括:

  1. 类型安全‌:

    • 编译时类型检查
    • 面向对象编程接口
  2. 高效序列化‌:

    • 使用专用Encoder编码器
    • 比Java序列化更高效
  3. 统一API‌:

    • 支持函数式转换和SQL操作
    • 可无缝转换为DataFrame

版本演进‌:
RDD(Spark1.0)→DataFrame(Spark1.3)→Dataset(Spark1.6)

三种数据类型的共性特征

  • 都是分布式弹性数据集
  • 都有惰性机制:在进行创建、转换,如map方法时,不会立即执行,只有在遇到Action如foreach时如collectcount等被调用时,Spark才会触发实际的计算
  • 都支持自动缓存
  • 都有partition概念:Partition是RDD(Resilient Distributed Dataset)的基本单位,代表了一个分区的数据。每个partition包含了一部分数据,Spark通过并行处理这些partition来提高处理速度和效率‌

主要区别

特性RDDDataFrameDataset
类型安全
优化 Catalyst优化 Catalyst优化
序列化 Java/Kryo Tungsten Encoder
API风格 函数式 SQL/DSL 类型安全DSL

3、PySpark

Spark与Hadoop(HDFS)的关系可以总结为:Spark主要替代Hadoop中的计算组件(MapReduce),而继续利用Hadoop的存储组件(HDFS),共同构成现代大数据处理的基础架构

PySpark是Apache Spark为Python开发者提供的编程接口,它结合了Python的简洁性和Spark的高性能分布式计算能力,使得用户能够用熟悉的Python语言处理大规模数据。以下是关于PySpark的关键点:

  1. 核心功能

    • 分布式数据处理:支持PB级数据量的处理,可从HDFS、HBase等多种数据源读取数据
    • 高性能计算:基于Spark的分布式框架和内存计算,速度远超传统磁盘计算
    • 易用性:提供Python API,支持交互式查询和实时数据分析
  2. 技术背景

    • PySpark底层由Scala编写,运行在JVM上,通过Py4J实现Python与Spark的交互
    • 作为Spark的Python实现,它既可作为第三方库使用,也可提交到Spark集群执行
  3. 应用场景

    • 适用于数据清洗、机器学习、实时处理等领域,广泛应用于金融、电商等行业
    • 典型用例包括分布式机器学习(如Spark MLlib)和大规模ETL任务

PySpark的优势在于其简洁的语法、高效的并行处理能力以及强大的生态支持,是Python开发者进入大数据领域的首选工具。

 

二、常用方法总结

1、创建Spark session

from pyspark.sql import SparkSession

# 创建SparkSession
spark = (
    SparkSession.builder
    .master("local")
    .appName("Word Count")
    .config("spark.some.config.option", "some-value")
    .getOrCreate()
)

各参数用法如下:

  1. master()
    指定Spark集群的运行模式,常见取值:

    • "local":本地单线程模式
    • "local[*]":本地模式并使用所有CPU核心
    • "spark://host:port":连接远程Spark集群
    • "yarn":在YARN集群上运行
  2. appName()
    设置应用程序名称,会显示在Spark Web UI(默认localhost:4040)上。若未指定会随机生成。例如这里的"Word Count"是用户自定义标识。

  3. config()
    用于设置Spark配置参数,支持两种方式:

    • 键值对形式:如.config("spark.some.config.option", "some-value")
    • 通过SparkConf对象传递:.config(conf=SparkConf())
      常用配置包括内存分配、并行度等
  4. getOrCreate()
    核心方法,若存在现有SparkSession则复用,否则新建。确保同一JVM中不会创建多个SparkContext

完整流程说明:
通过SparkSession.builder链式调用配置参数,最终执行getOrCreate()完成初始化。此模式支持本地测试(如master("local"))和生产环境(如YARN/K8s)的统一编程接口

2、RDD操作

RDD(弹性分布式数据集)是Spark中最基本的数据结构,具有以下特点:

  • 不可变分布式集合
  • 支持并行操作
  • 自动容错恢复
  • 可分区存储

增(Create)

from pyspark.sql import SparkSession

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从集合创建RDD
data = [1, 2, 3, 4, 5]
rdd1 = spark.sparkContext.parallelize(data)
print(rdd1.collect())  # [1, 2, 3, 4, 5]

# 从文件创建RDD
"""rddText.txt内容如下
Hello Spark
This is a test file
Line 3
"""
rdd2 = spark.sparkContext.textFile(r"E:\rddText.txt")
print(rdd2.collect())  # ['Hello Spark', 'This is a test file', 'Line 3']

查(Read)

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从集合创建RDD
data = [6, 1, 2, 3, 4, 5]
rdd = spark.sparkContext.parallelize(data)

# 查看前n个元素
top_n = rdd.take(3)  # 没有排序,按照原本的顺便输出
print(top_n)  # [6, 1, 2]

# 获取排序后的前n个元素(默认升序)
sorted_top_n = rdd.takeOrdered(3)  # 对于数字RDD,默认已经是升序的
print(sorted_top_n)  # [1, 2, 3]

sorted_top_n_desc = rdd.takeOrdered(3, key=lambda x: -x)  # 降序
print(sorted_top_n_desc)  # [6, 5, 4]

# 查看所有元素(谨慎使用,数据量大时会内存溢出)
all_data = rdd.collect()
print(all_data)  # [6, 1, 2, 3, 4, 5]

# 过滤数据
filtered_rdd = rdd.filter(lambda x: x > 3)
print(filtered_rdd.collect())  # [6, 4, 5]

# 映射转换
mapped_rdd = rdd.map(lambda x: x * 2)
print(mapped_rdd.collect())  # [12, 2, 4, 6, 8, 10]

改(Update)

RDD本身不可变,修改操作实际上是生成新的RDD

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从集合创建RDD
data = [6, 1, 2, 3, 4, 5]
rdd = spark.sparkContext.parallelize(data)

# 通过map转换创建新RDD
new_rdd = rdd.map(lambda x: x + 10)
print(new_rdd.collect())  # [16, 11, 12, 13, 14, 15]

# 通过union合并RDD
rdd1 = spark.sparkContext.parallelize([1, 2, 3])
rdd2 = spark.sparkContext.parallelize([4, 5, 6])
combined_rdd = rdd1.union(rdd2)
print(combined_rdd.collect())  # [1, 2, 3, 4, 5, 6]

删(Delete)

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从集合创建RDD
data = [6, 1, 2, 3, 4, 5]
rdd = spark.sparkContext.parallelize(data)

# 通过filter实现删除
filtered_rdd = rdd.filter(lambda x: x != 3)  # 删除值为3的元素
print(filtered_rdd.collect())  # [6, 1, 2, 4, 5]

3、DataFrame操作

DataFrame是以列形式组织的分布式数据集,具有schema信息。

增(Create)

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从文件创建
df1 = spark.read.csv(r"E:\test.csv", header=True)

# 从RDD创建
data = [("Alice", 1), ("Bob", 2)]
rdd = spark.sparkContext.parallelize(data)
df2 = rdd.toDF(["name", "age"])  # rdd转dataframe
df2.show()
"""
+-----+---+
| name|age|
+-----+---+
|Alice|  1|
|  Bob|  2|
+-----+---+
"""

# createDataFrame创建
df22 = spark.createDataFrame([("Alice", 1), ("Bob", 2)], schema=["name", "age"])
df22.show()
"""
+-----+---+
| name|age|
+-----+---+
|Alice|  1|
|  Bob|  2|
+-----+---+
"""

# 添加新列
df3 = df2.withColumn("age_plus_10", df2["age"] + 10)
df3.show()
"""
+-----+---+-----------+
| name|age|age_plus_10|
+-----+---+-----------+
|Alice|  1|         11|
|  Bob|  2|         12|
+-----+---+-----------+
"""

查(Read)

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从文件创建
df = spark.read.csv(r"E:\test.csv", header=True)

# 展示全部
df.show()

# 显示前n行
df.show(5)

# 查看schema
df.printSchema()

# 选择特定列
df.select("name").show()

# 条件查询
df.filter(df["age"] > 18).show()

# 排序
df.sort("age", ascending=False).show()

改(Update)

import os
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, when, upper

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从文件创建
df = spark.read.csv(r"E:\test.csv", header=True)

# 基于条件修改列值
df = df.withColumn("age", when(col("age") > 30, col("age") + 1).otherwise(col("age")))
df.show()

# 重命名列
df = df.withColumnRenamed("age", "user_age")

df = df.withColumnRenamed("user_age", "age")

# 更新多列
df = df.withColumn("age", df["age"] + 1) \
    .withColumn("name", upper(df["name"]))
df.show()

删(Delete)

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 从文件创建
df = spark.read.csv(r"E:\test.csv", header=True)

# 删除列
df = df.drop("name")

# 删除行(通过过滤)
df = df.filter(df["age"] != 30)
df.show()

4、select("name")和select(col("name"))的区别

在PySpark中,df.select("name")df.select(col("name"))的主要区别在于语法形式和灵活性,但两者最终实现的效果是相同的。具体差异如下:

 

  1. 语法形式

    • df.select("name"):直接使用字符串指定列名,属于简写形式
    • df.select(col("name")):通过col()函数显式创建列对象,需先导入from pyspark.sql.functions import col
  2. 使用场景差异

    • 字符串形式更简洁,适合简单列选择
    • col()函数在以下场景更灵活:
      • 需要列运算时(如col("age") + 10
      • 处理带特殊字符的列名(如col("a.column.with.dots")
      • 链式调用时保持代码一致性
  3. 底层实现
    两种方式最终都会转换为相同的逻辑计划,性能无差异。字符串形式会在内部自动转换为Column对象。

建议根据代码上下文选择:简单场景用字符串,复杂表达式或需要明确列对象时用col()

5、RDD与DataFrame相互转换

RDD转DataFrame

import os
from pyspark.sql import SparkSession, Row

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

rdd = spark.sparkContext.parallelize([("Alice", 29), ("Bob", 31)])
df1 = rdd.toDF(["name","age"])  # 通过toDF方法简单转换
df2 = rdd.map(lambda x: Row(name=x[0], age=x[1])).toDF()  # # 通过Row对象和toDF方法进行转换

DataFrame转RDD

import os
from pyspark.sql import SparkSession

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

df = spark.read.csv(r"E:\test.csv")
rdd = df.rdd  # 返回RDD[Row]对象

6、其他常用方法

import os
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, lit, when, concat, concat_ws, trim, lower, upper, abs, sqrt, count, countDistinct, sum, avg, max

os.environ['PYSPARK_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'
os.environ['PYSPARK_DRIVER_PYTHON'] = r'C:\Python\Pyhton3.9\python.exe'

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

# 读取文本文件为RDD: path(文件路径)
rdd = spark.sparkContext.textFile("data.txt")

# 读取CSV文件为DataFrame: path(文件路径), header(是否包含表头), inferSchema(是否推断数据类型), sep(分隔符)
df = spark.read.csv("data.csv", header=True, inferSchema=True)

# 读取Parquet格式文件: path(文件路径)
d2 = spark.read.parquet("data.parquet")

# 读取JSON文件: path(文件路径)
d3 = spark.read.json("data.json")

# 指定读取的文件格式: format(文件格式)
d4 = spark.read.format("csv").load("data.csv")

# 设置数据写入模式: mode(写入模式:append/overwrite/ignore/error)
df.write.mode("overwrite").csv("output.csv")

# 选择列并重命名
df.select(col("name").alias("username"))

# 排序数据
df.orderBy(col("age").desc())

# 创建包含常量值的列
df.withColumn("constant_col", lit(1))  # 添加值为1的常量列
df.withColumn("text_col", lit("default_value"))  # 字符串常量
df.withColumn("flag_col", lit(True))  # 布尔值常量
df.withColumn("price_col", lit(99.9))  # 浮点数常量
df.withColumn("status", when(df.age > 18, lit("adult")).otherwise(lit("child")))  # 与when结合使用

# 多条件分支判断
df.withColumn("result",
              when(col("age") > 60, "old man")
              .when(col("age") < 18, "younger")
              .otherwise("no old no younger")
              )
# 字符串拼接: concat:拼接多列( 没有分隔符);concat_ws:拼接多列(带自定义的分隔符)
df.select(concat(col("name"), col("age"))).show()
df.select(concat_ws("-", col("name"), col("age"))).show()
"""
+-----------------+
|concat(name, age)|
+-----------------+
|           牛哥18|
+-----------------+

+-----------------------+
|concat_ws(-, name, age)|
+-----------------------+
|                牛哥-18|
+-----------------------+
"""

# 处理DataFrame中的缺失值: 参数1-填充值, 参数2-可选列名
df.na.fill(50)  # 用固定值填充所有列的缺失值

df.na.fill(0, ["age", "height"])  # 指定列填充‌: 仅填充age和height列

fill_dict = {"age": 0, "name": "N/A"}
df.na.fill(fill_dict)  # 为不同列指定不同填充值:

# df.na.drop()用于处理DataFrame中的缺失值(NA/Null)
df.na.drop()  # 删除任何包含NA的行(默认行为)该方法会返回一个新DataFrame,原始DataFrame不会被修改

df.na.drop(subset=["col1", "col2"])  # 仅当指定列包含NA时才删除行

df.na.drop(how="all")  # 仅当整行全为NA时才删除, how参数支持"any"(默认)和"all"两种模式

# df.join: 连接两个DataFrame,参数: 另一个DataFrame, 连接条件, 连接类型
# df1.join(df2, df1.id == df2.id, "inner")

# 将DataFrame转换为RDD后收集为列表
df.select("name").rdd.flatMap(lambda x: x).collect()

# 字符串格式化: trim() / lower() / upper()
df.withColumn("name_clean", trim(lower(col("name"))))

# 数学运算‌: 绝对值abs、平方根sqrt、立方根cbrt、均值avg、四舍五入round、向上取整ceil、向下取整floor
df.select(abs(col("age")), sqrt(col("age")))

# 聚合函数‌: 计数count、去重计数countDistinct、基础聚合运算:sum() / avg() / min() / max()
df.groupBy("dept").agg(count("*"), countDistinct("user"))

# 基础聚合操作示例
# 示例:按用户ID分组,计算总金额和平均金额
df.groupBy("user_id").agg(
    sum("amount").alias("total_amount"),
    avg("amount").alias("avg_amount")
)
# 对金额列求和,对年龄列求最大值: 通过字典指定不同列的聚合方式(字典键为列名,值为聚合函数名)
df.groupBy("user_id").agg({
    "amount": "sum",
    "age": "max"
})
# 全局聚合(无分组)‌直接对整个DataFrame应用聚合(此时无需调用groupBy)
# 计算所有金额的总和和最大值
df.agg(
    sum("amount").alias("total"),
    max("amount").alias("peak")
)

7、合并相同的dataFrame

在PySpark中,可以使用unionAllreduce来合并多个DataFrame。unionAll用于垂直合并(按行追加),而reduce可以简化多个DataFrame的合并过程.

from pyspark.sql import SparkSession
from pyspark.sql import DataFrame
from functools import reduce

# 创建SparkSession
spark = SparkSession.builder.appName("zzz").getOrCreate()

df1 = spark.createDataFrame([(1, "A"), (2, "B")], ["id", "col1"])
df2 = spark.createDataFrame([(3, "C"), (4, "D")], ["id", "col1"])
df3 = spark.createDataFrame([(5, "E"), (6, "F")], ["id", "col1"])
dfs = [df1, df2, df3]
# 使用reduce简洁地实现
combined_df = reduce(DataFrame.unionAll, dfs)
combined_df.show()

 

三、spark.sql基础

1、表的操作

from pyspark.sql import SparkSession
spark = SparkSession.builder.enableHiveSupport().getOrCreate()

# 创建表
spark.sql("CREATE TABLE IF NOT EXISTS employees (id INT, name STRING, salary FLOAT)")

# 修改表结构
spark.sql("ALTER TABLE employees ADD COLUMN department STRING")

# 删除表
spark.sql("DROP TABLE IF EXISTS temp_employees")

# 查询表信息
spark.sql("SHOW TABLES").show()
spark.sql("DESCRIBE employees").show()

 

2、记录的操作

from pyspark.sql import SparkSession
spark = SparkSession.builder.enableHiveSupport().getOrCreate()
 
插入记录
spark.sql("INSERT INTO employees VALUES (101, '张三', 8500.0, 'IT')")

# 批量插入
spark.sql("""
  INSERT INTO employees 
  VALUES (102, '李四', 9200.0, 'HR'),
         (103, '王五', 7800.0, 'Finance')
""")

# 更新记录
spark.sql("UPDATE employees SET salary = salary*1.1 WHERE department = 'IT'")

# 删除记录
spark.sql("DELETE FROM employees WHERE salary < 8000")

# 查询记录
spark.sql("SELECT * FROM employees WHERE department = 'IT'").show()

 

posted @ 2025-05-28 19:42  我用python写Bug  阅读(135)  评论(0)    收藏  举报