DataFrame快速入门

本文介绍了 PySpark DataFrame 的基本概念与操作方法,包括 DataFrame 的创建、展示、选择和访问数据、应用函数、数据分组及数据输入输出等。同时,文章还演示了如何在 PySpark 中使用 SQL 进行数据查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章最前: 我是Octopus,这个名字来源于我的中文名--章鱼;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github ;这博客是记录我学习的点点滴滴,如果您对 Python、Java、AI、算法有兴趣,可以关注我的动态,一起学习,共同进步。

   相关文章:

  1. PySpark 概述
  2. Spark连接快速入门
  3. Spark上使用pandas API快速入门

这是 PySpark DataFrame API 的简短介绍和快速入门。PySpark DataFrame 是延迟评估的。它们是在RDD之上实现的。当 Spark转换数据时,它不会立即计算转换,而是计划稍后如何计算。当 显式调用诸如此类的操作collect()时,计算就会开始。本笔记本展示了 DataFrame 的基本用法。您可以在快速入门页面的“Live Notebook:DataFrame”中自行运行这些示例的最新版本。

Apache Spark 文档站点中还有其他有用的信息,请参阅最新版本的Spark SQL 和 DataFramesRDD 编程指南结构化流编程指南Spark Streaming 编程指南机器学习库 (MLlib) 指南

PySpark 应用程序从初始化开始,SparkSession这是 PySpark 的入口点,如下所示。如果通过 pyspark 可执行文件在 PySpark shell 中运行它,shell 会自动在变量 Spark 中为用户创建会话。

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

 pyspark DataFrame的创建

PySpark DataFramepyspark.sql.SparkSession.createDataFrame通常可以通过传递列表、元组、字典和pyspark.sql.Rows 的列表、pandas DataFrame和由此类列表组成的 RDD 来创建。pyspark.sql.SparkSession.createDataFrame使用schema参数来指定 DataFrame 的架构。当省略时,PySpark 通过从数据中采样来推断相应的模式。

首先,您可以从行列表创建一个 PySpark DataFrame

from datetime import datetime, date
import pandas as pd
from pyspark.sql import Row

df = spark.createDataFrame([
    Row(a=1, b=2., c='string1', d=date(2000, 1, 1), e=datetime(2000, 1, 1, 12, 0)),
    Row(a=2, b=3., c='string2', d=date(2000, 2, 1), e=datetime(2000, 1, 2, 12, 0)),
    Row(a=4, b=5., c='string3', d=date(2000, 3, 1), e=datetime(2000, 1, 3, 12, 0))
])
df
DataFrame[a: bigint, b: double, c: string, d: date, e: timestamp]

显示创建PySpark DataFrame

df = spark.createDataFrame([
    (1, 2., 'string1', date(2000, 1, 1), datetime(2000, 1, 1, 12, 0)),
    (2, 3., 'string2', date(2000, 2, 1), datetime(2000, 1, 2, 12, 0)),
    (3, 4., 'string3', date(2000, 3, 1), datetime(2000, 1, 3, 12, 0))
], schema='a long, b double, c string, d date, e timestamp')
df
DataFrame[a: bigint, b: double, c: string, d: date, e: timestamp]

通过pandas创建pyspark DataFrame

pandas_df = pd.DataFrame({
    'a': [1, 2, 3],
    'b': [2., 3., 4.],
    'c': ['string1', 'string2', 'string3'],
    'd': [date(2000, 1, 1), date(2000, 2, 1), date(2000, 3, 1)],
    'e': [datetime(2000, 1, 1, 12, 0), datetime(2000, 1, 2, 12, 0), datetime(2000, 1, 3, 12, 0)]
})
df = spark.createDataFrame(pandas_df)
df
DataFrame[a: bigint, b: double, c: string, d: date, e: timestamp

上面创建的 DataFrame 都具有相同的结果和架构。

# All DataFrames above result same.
df.show()
df.printSchema()
+---+---+-------+----------+-------------------+
|  a|  b|      c|         d|                  e|
+---+---+-------+----------+-------------------+
|  1|2.0|string1|2000-01-01|2000-01-01 12:00:00|
|  2|3.0|string2|2000-02-01|2000-01-02 12:00:00|
|  3|4.0|string3|2000-03-01|2000-01-03 12:00:00|
+---+---+-------+----------+-------------------+

root
 |-- a: long (nullable = true)
 |-- b: double (nullable = true)
 |-- c: string (nullable = true)
 |-- d: date (nullable = true)
 |-- e: timestamp (nullable = true)

展示数据

DataFrame 的顶行可以使用 显示DataFrame.show()

df.show(1)
+---+---+-------+----------+-------------------+
|  a|  b|      c|         d|                  e|
+---+---+-------+----------+-------------------+
|  1|2.0|string1|2000-01-01|2000-01-01 12:00:00|
+---+---+-------+----------+-------------------+
only showing top 1 row

或者,您可以启用spark.sql.repl.eagerEval.enabled配置以在 Jupyter 等笔记本中对 PySpark DataFrame 进行即时评估。可以通过配置控制要显示的行数spark.sql.repl.eagerEval.maxNumRows

spark.conf.set('spark.sql.repl.eagerEval.enabled', True)
df
abcde
12.0string12000-01-012000-01-01 12:00:00
23.0string22000-02-012000-01-02 12:00:00
34.0string32000-03-012000-01-03 12:00:00

行也可以垂直显示。当行太长而无法水平显示时,这非常有用。

df.show(1, vertical=True)
-RECORD 0------------------
 a   | 1
 b   | 2.0
 c   | string1
 d   | 2000-01-01
 e   | 2000-01-01 12:00:00
only showing top 1 row

显示spark的结构和列名:

df.columns
['a', 'b', 'c', 'd', 'e']
df.printSchema()
root
 |-- a: long (nullable = true)
 |-- b: double (nullable = true)
 |-- c: string (nullable = true)
 |-- d: date (nullable = true)
 |-- e: timestamp (nullable = true)

显示pyspark DataFrame的详情:

df.select("a", "b", "c").describe().show()
+-------+---+---+-------+
|summary|  a|  b|      c|
+-------+---+---+-------+
|  count|  3|  3|      3|
|   mean|2.0|3.0|   null|
| stddev|1.0|1.0|   null|
|    min|  1|2.0|string1|
|    max|  3|4.0|string3|
+-------+---+---+-------+

DataFrame.collect()将分布式数据采集到驱动端作为Python本地数据。请注意,当数据集太大而无法容纳驱动程序端时,这可能会引发内存不足错误,因为它会将所有数据从执行程序收集到驱动程序端。

df.collect()
[Row(a=1, b=2.0, c='string1', d=datetime.date(2000, 1, 1), e=datetime.datetime(2000, 1, 1, 12, 0)), Row 
 ( a=2, b=3.0, c='string2', d=datetime.date(2000, 2, 1), e=datetime.datetime(2000, 1, 2, 12, 0)), Row(a= 
 3 , b=4.0, c='string3', d=datetime.date(2000, 3, 1), e=datetime.datetime(2000, 1, 3, 12, 0))]

为了避免引发内存不足异常,请使用DataFrame.take()DataFrame.tail()

df.take(1)
[Row(a=1, b=2.0, c='string1', d=datetime.date(2000, 1, 1), e=datetime.datetime(2000, 1, 1, 12, 0))]

PySpark DataFrame 还提供了到pandas DataFrame 的转换以利用 pandas API。请注意,toPandas还将所有数据收集到驱动程序端,当数据太大而无法放入驱动程序端时,很容易导致内存不足错误。

df.toPandas()
abcde
012.0string12000-01-012000-01-01 12:00:00
123.0string22000-02-012000-01-02 12:00:00
234.0string32000-03-012000-01-03 12:00:00

选择和访问数据

PySpark DataFrame 是延迟计算的,简单地选择一列不会触发计算,但会返回一个Column实例。

df.a
Column<b'a'>

事实上,大多数按列运算都会返回Columns。

from pyspark.sql import Column
from pyspark.sql.functions import upper

type(df.c) == type(upper(df.c)) == type(df.c.isNull())
true
这些 Column可用于从 DataFrame 中选择列。例如, DataFrame.select()获取 Column返回另一个 DataFrame 的实例。
df.select(df.c).show()
+-------+
|      c|
+-------+
|string1|
|string2|
|string3|
+-------+

分配新Column实例。

df.withColumn('upper_c', upper(df.c)).show()
+---+---+-------+----------+-------------------+-------+
|  a|  b|      c|         d|                  e|upper_c|
+---+---+-------+----------+-------------------+-------+
|  1|2.0|string1|2000-01-01|2000-01-01 12:00:00|STRING1|
|  2|3.0|string2|2000-02-01|2000-01-02 12:00:00|STRING2|
|  3|4.0|string3|2000-03-01|2000-01-03 12:00:00|STRING3|
+---+---+-------+----------+-------------------+-------+

使用DataFrame.filter()过滤一些子集

df.filter(df.a == 1).show()
+---+---+-------+----------+-------------------+
|  a|  b|      c|         d|                  e|
+---+---+-------+----------+-------------------+
|  1|2.0|string1|2000-01-01|2000-01-01 12:00:00|
+---+---+-------+----------+-------------------+

应用函数

PySpark 支持各种 UDF 和 API,以允许用户执行 Python 原生函数。另请参阅最新的Pandas UDFPandas Function API例如,下面的示例允许用户在Python本机函数中直接使用pandas系列中的API 。

import pandas as pd
from pyspark.sql.functions import pandas_udf

@pandas_udf('long')
def pandas_plus_one(series: pd.Series) -> pd.Series:
    # Simply plus one by using pandas Series.
    return series + 1

df.select(pandas_plus_one(df.a)).show()
+------------------+ 
|pandas_plus_one(a)| 
+------------------+ 
| 2| 
| 3| 
| 4| 
+------------------+

另一个例子是允许用户直接使用pandas DataFrameDataFrame.mapInPandas中的 API,而没有任何限制,例如结果长度。

def pandas_filter_func(iterator):
    for pandas_df in iterator:
        yield pandas_df[pandas_df.a == 1]

df.mapInPandas(pandas_filter_func, schema=df.schema).show()
+---+---+-------+----------+-------------------+
|  a|  b|      c|         d|                  e|
+---+---+-------+----------+-------------------+
|  1|2.0|string1|2000-01-01|2000-01-01 12:00:00|
+---+---+-------+----------+-------------------+

数据分组

PySpark DataFrame 还提供了一种使用常见方法“拆分-应用-组合”策略来处理分组数据的方法。它按特定条件对数据进行分组,对每个组应用一个函数,然后将它们组合回 DataFrame。

df = spark.createDataFrame([
    ['red', 'banana', 1, 10], ['blue', 'banana', 2, 20], ['red', 'carrot', 3, 30],
    ['blue', 'grape', 4, 40], ['red', 'carrot', 5, 50], ['black', 'carrot', 6, 60],
    ['red', 'banana', 7, 70], ['red', 'grape', 8, 80]], schema=['color', 'fruit', 'v1', 'v2'])
df.show()
+-----+------+---+---+
|color| fruit| v1| v2|
+-----+------+---+---+
|  red|banana|  1| 10|
| blue|banana|  2| 20|
|  red|carrot|  3| 30|
| blue| grape|  4| 40|
|  red|carrot|  5| 50|
|black|carrot|  6| 60|
|  red|banana|  7| 70|
|  red| grape|  8| 80|
+-----+------+---+---+

分组,然后将avg()函数应用到结果组。

df.groupby('color').avg().show()
+-----+-------+-------+
|color|avg(v1)|avg(v2)|
+-----+-------+-------+
|  red|    4.8|   48.0|
|black|    6.0|   60.0|
| blue|    3.0|   30.0|
+-----+-------+-------+

还可以使用 pandas API 对每个组应用 Python 本机函数。

def plus_mean(pandas_df):
    return pandas_df.assign(v1=pandas_df.v1 - pandas_df.v1.mean())

df.groupby('color').applyInPandas(plus_mean, schema=df.schema).show()
+-----+------+---+---+
|color| fruit| v1| v2|
+-----+------+---+---+
|  red|banana| -3| 10|
|  red|carrot| -1| 30|
|  red|carrot|  0| 50|
|  red|banana|  2| 70|
|  red| grape|  3| 80|
|black|carrot|  0| 60|
| blue|banana| -1| 20|
| blue| grape|  1| 40|
+-----+------+---+---+

共同分组并应用函数。

df1 = spark.createDataFrame(
    [(20000101, 1, 1.0), (20000101, 2, 2.0), (20000102, 1, 3.0), (20000102, 2, 4.0)],
    ('time', 'id', 'v1'))

df2 = spark.createDataFrame(
    [(20000101, 1, 'x'), (20000101, 2, 'y')],
    ('time', 'id', 'v2'))

def merge_ordered(l, r):
    return pd.merge_ordered(l, r)

df1.groupby('id').cogroup(df2.groupby('id')).applyInPandas(
    merge_ordered, schema='time int, id int, v1 double, v2 string').show()
+--------+---+---+---+
|    time| id| v1| v2|
+--------+---+---+---+
|20000101|  1|1.0|  x|
|20000102|  1|3.0|  x|
|20000101|  2|2.0|  y|
|20000102|  2|4.0|  y|
+--------+---+---+---+

数据输入和输出

CSV 简单且易于使用。Parquet 和 ORC 是高效且紧凑的文件格式,读写速度更快。

PySpark 中还有许多其他可用的数据源,例如 JDBC、文本、binaryFile、Avro 等。另请参阅 Apache Spark 文档中的最新Spark SQL、数据帧和数据集指南

CSV

df.write.csv('foo.csv', header=True)
spark.read.csv('foo.csv', header=True).show()
+-----+------+---+---+
|color| fruit| v1| v2|
+-----+------+---+---+
|  red|banana|  1| 10|
| blue|banana|  2| 20|
|  red|carrot|  3| 30|
| blue| grape|  4| 40|
|  red|carrot|  5| 50|
|black|carrot|  6| 60|
|  red|banana|  7| 70|
|  red| grape|  8| 80|
+-----+------+---+---+

Parquet

df.write.parquet('bar.parquet')
spark.read.parquet('bar.parquet').show()
+-----+------+---+---+
|color| fruit| v1| v2|
+-----+------+---+---+
|  red|banana|  1| 10|
| blue|banana|  2| 20|
|  red|carrot|  3| 30|
| blue| grape|  4| 40|
|  red|carrot|  5| 50|
|black|carrot|  6| 60|
|  red|banana|  7| 70|
|  red| grape|  8| 80|
+-----+------+---+---+

ORC

df.write.orc('zoo.orc')
spark.read.orc('zoo.orc').show()
+-----+------+---+---+
|color| fruit| v1| v2|
+-----+------+---+---+
|  red|banana|  1| 10|
| blue|banana|  2| 20|
|  red|carrot|  3| 30|
| blue| grape|  4| 40|
|  red|carrot|  5| 50|
|black|carrot|  6| 60|
|  red|banana|  7| 70|
|  red| grape|  8| 80|
+-----+------+---+---+

使用SQL

DataFrame 和 Spark SQL 共享相同的执行引擎,因此它们可以无缝地互换使用。例如,您可以将 DataFrame 注册为表并轻松运行 SQL,如下所示:

df.createOrReplaceTempView("tableA")
spark.sql("SELECT count(*) from tableA").show()
+--------+
|count(1)|
+--------+
|       8|
+--------+

此外,UDF 可以直接在 SQL 中注册和调用:

@pandas_udf("integer")
def add_one(s: pd.Series) -> pd.Series:
    return s + 1

spark.udf.register("add_one", add_one)
spark.sql("SELECT add_one(v1) FROM tableA").show()
+------------+ 
|add_one(v1)| 
+------------+ 
| 2| 
| 3| 
| 4| 
| 5| 
| 6| 
| 7| 
| 8| 
| 9| 
+------------+

这些 SQL 表达式可以直接混合并用作 PySpark 列。

from pyspark.sql.functions import expr

df.selectExpr('add_one(v1)').show()
df.select(expr('count(*)') > 0).show()
+-----------+
|add_one(v1)|
+-----------+
|          2|
|          3|
|          4|
|          5|
|          6|
|          7|
|          8|
|          9|
+-----------+

+--------------+
|(count(1) > 0)|
+--------------+
|          true|
+--------------+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值