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为基础的分布式数据集,类似于传统数据库中的二维表格。主要特点包括:
-
结构化特性:
- 带有schema元信息(列名和类型)
- 每行类型固定为Row对象
-
性能优化:
- 支持off-heap存储,减少GC开销
- 支持执行计划优化(Catalyst优化器)
-
API优势:
- 提供高层关系操作,比RDD API更友好
- 支持SQL查询接口
DataFrame与RDD对比:
- RDD不了解数据结构,DataFrame有详细schema信息
- RDD是分布式Java对象集合,DataFrame是分布式Row对象集合
Dataset是Spark 1.6引入的API,结合了RDD和DataFrame的优点。核心特性包括:
-
类型安全:
- 编译时类型检查
- 面向对象编程接口
-
高效序列化:
- 使用专用Encoder编码器
- 比Java序列化更高效
-
统一API:
- 支持函数式转换和SQL操作
- 可无缝转换为DataFrame
版本演进:
RDD(Spark1.0)→DataFrame(Spark1.3)→Dataset(Spark1.6)
三种数据类型的共性特征
- 都是分布式弹性数据集
- 都有惰性机制:在进行创建、转换,如map方法时,不会立即执行,只有在遇到Action如foreach时如
collect、count等被调用时,Spark才会触发实际的计算 - 都支持自动缓存
- 都有partition概念:Partition是RDD(Resilient Distributed Dataset)的基本单位,代表了一个分区的数据。每个partition包含了一部分数据,Spark通过并行处理这些partition来提高处理速度和效率。
主要区别
| 特性 | RDD | DataFrame | Dataset |
|---|---|---|---|
| 类型安全 | 是 | 否 | 是 |
| 优化 | 无 | 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的关键点:
-
核心功能
- 分布式数据处理:支持PB级数据量的处理,可从HDFS、HBase等多种数据源读取数据。
- 高性能计算:基于Spark的分布式框架和内存计算,速度远超传统磁盘计算。
- 易用性:提供Python API,支持交互式查询和实时数据分析。
-
技术背景
- PySpark底层由Scala编写,运行在JVM上,通过Py4J实现Python与Spark的交互。
- 作为Spark的Python实现,它既可作为第三方库使用,也可提交到Spark集群执行。
-
应用场景
- 适用于数据清洗、机器学习、实时处理等领域,广泛应用于金融、电商等行业。
- 典型用例包括分布式机器学习(如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() )
各参数用法如下:
-
master()
指定Spark集群的运行模式,常见取值:"local":本地单线程模式"local[*]":本地模式并使用所有CPU核心"spark://host:port":连接远程Spark集群"yarn":在YARN集群上运行
-
appName()
设置应用程序名称,会显示在Spark Web UI(默认localhost:4040)上。若未指定会随机生成。例如这里的"Word Count"是用户自定义标识。 -
config()
用于设置Spark配置参数,支持两种方式:- 键值对形式:如
.config("spark.some.config.option", "some-value") - 通过SparkConf对象传递:
.config(conf=SparkConf())
常用配置包括内存分配、并行度等。
- 键值对形式:如
-
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"))的主要区别在于语法形式和灵活性,但两者最终实现的效果是相同的。具体差异如下:
-
语法形式
df.select("name"):直接使用字符串指定列名,属于简写形式。df.select(col("name")):通过col()函数显式创建列对象,需先导入from pyspark.sql.functions import col
-
使用场景差异
- 字符串形式更简洁,适合简单列选择
col()函数在以下场景更灵活:- 需要列运算时(如
col("age") + 10) - 处理带特殊字符的列名(如
col("a.column.with.dots")) - 链式调用时保持代码一致性
- 需要列运算时(如
-
底层实现
两种方式最终都会转换为相同的逻辑计划,性能无差异。字符串形式会在内部自动转换为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中,可以使用unionAll和reduce来合并多个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()

浙公网安备 33010602011771号