PySpark Usage Guide for Pandas with Apache Arrow(使用Apache Arrow的Pandas PySpark使用指南)
- Apache Arrow in Spark
- Ensure PyArrow Installed
- Enabling for Conversion to/from Pandas
- Pandas UDFs (a.k.a. Vectorized UDFs)
- Scalar
- Grouped Map
- Grouped Aggregate
- Usage Notes
- Supported SQL Types
- Setting Arrow Batch Size
- Timestamp with Time Zone Semantics
Apache Arrow in Spark(Spark中的Apache Arrow)
·Apache Arrow是一种内存中的列式数据格式,在Spark中用于在JVM和Python进程之间有效地传输数据。
·这对于使用Pandas / NumPy数据的Python用户来说是最有益的。
·它的使用不是自动的,可能需要对配置或代码进行一些小的更改才能充分利用并确保兼容性。
·本指南将提供有关如何在Spark中使用Arrow的高级描述,并在使用启用箭头的数据时突出显示任何差异。
Ensure PyArrow Installed(确保PyArrow已安装)
·如果使用pip安装PySpark,则可以使用命令pip install pyspark [sql]将PyArrow作为SQL模块的额外依赖项引入。
·否则,您必须确保在所有群集节点上安装并可用PyArrow。
·当前支持的版本是0.8.0。
·您可以使用conda-forge通道中的pip或conda进行安装。
·有关详细信息,请参阅PyArrow安装。
Enabling for Conversion to/from Pandas(启用与Pandas的转换)
·使用调用toPandas()将Spark DataFrame转换为Pandas DataFrame时以及使用createDataFrame(pandas_df)从Pandas DataFrame创建Spark DataFrame时,Arrow可用作优化。
·要在执行这些调用时使用Arrow,用户需要先将Spark配置'spark.sql.execution.arrow.enabled'设置为'true'。
·默认情况下禁用此功能。
·此外,如果在Spark中的实际计算之前发生错误,则由'spark.sql.execution.arrow.enabled'启用的优化可以自动回退到非Arrow优化实现。
·这可以通过'spark.sql.execution.arrow.fallback.enabled'来控制。
import numpy as np
import pandas as pd # Enable Arrow-based columnar data transfers spark.conf.set("spark.sql.execution.arrow.enabled", "true") # Generate a Pandas DataFrame pdf = pd.DataFrame(np.random.rand(100, 3)) # Create a Spark DataFrame from a Pandas DataFrame using Arrow df = spark.createDataFrame(pdf) # Convert the Spark DataFrame back to a Pandas DataFrame using Arrow result_pdf = df.select("*").toPandas()
·在Spark repo中的“examples / src / main / python / sql / arrow.py”中找到完整的示例代码。
·使用上述箭头优化将产生与未启用箭头时相同的结果。
·请注意,即使使用Arrow,toPandas()也会将DataFrame中所有记录收集到驱动程序中,并且应该在一小部分数据上完成。
·当前不支持所有Spark数据类型,如果列具有不受支持的类型,则可能引发错误,请参阅支持的SQL类型。
·如果在createDataFrame()期间发生错误,Spark将回退以创建没有Arrow的DataFrame。
Pandas UDFs (a.k.a. Vectorized UDFs)
·Pandas UDF是用户定义的函数,由Spark使用Arrow执行传输数据和Pandas以处理数据。
·Pandas UDF使用关键字pandas_udf作为装饰器定义或包装函数,不需要其他配置。
·目前,有两种类型的Pandas UDF:Scalar和Grouped Map。
Scalar
·标量Pandas UDF用于矢量化标量操作。
·它们可以与select和withColumn等函数一起使用。
·Python函数应该将pandas.Series作为输入并返回相同长度的pandas.Series。
·在内部,Spark将执行Pandas UDF,方法是将列拆分为批次,并将每个批次的函数作为数据的子集调用,然后将结果连接在一起。
·以下示例显示如何创建计算2列乘积的标量Pandas UDF。
import pandas as pd
from pyspark.sql.functions import col, pandas_udf from pyspark.sql.types import LongType # Declare the function and create the UDF def multiply_func(a, b): return a * b multiply = pandas_udf(multiply_func, returnType=LongType()) # The function for a pandas_udf should be able to execute with local Pandas data x = pd.Series([1, 2, 3]) print(multiply_func(x, x)) # 0 1 # 1 4 # 2 9 # dtype: int64 # Create a Spark DataFrame, 'spark' is an existing SparkSession df = spark.createDataFrame(pd.DataFrame(x, columns=["x"])) # Execute function as a Spark vectorized UDF df.select(multiply(col("x"), col("x"))).show() # +-------------------+ # |multiply_func(x, x)| # +-------------------+ # | 1| # | 4| # | 9| # +-------------------+
Find full example code at "examples/src/main/python/sql/arrow.py" in the Spark repo.
Grouped Map(分组图)
·分组映射Pandas UDF与groupBy()。apply()一起使用,它实现了“split-apply-combine”模式。
·Split-apply-combine包含三个步骤:
·使用DataFrame.groupBy将数据拆分为组。
·在每个组上应用一个功能。
·该函数的输入和输出都是pandas.DataFrame。
·输入数据包含每个组的所有行和列。
·将结果合并到一个新的DataFrame中。
·要使用groupBy()。apply(),用户需要定义以下内容:
·一个Python函数,用于定义每个组的计算。
·StructType对象或定义输出DataFrame架构的字符串。
·如果指定为字符串,则返回的pandas.DataFrame的列标签必须与定义的输出模式中的字段名称匹配,或者如果不是字符串,则必须按字段匹配字段数据类型,例如,
·整数指数。
·有关如何在构造pandas.DataFrame时标记列的信息,请参阅pandas.DataFrame。
·请注意,在应用函数之前,组的所有数据都将加载到内存中。
·这可能导致内存不足异常,尤其是在组大小偏斜的情况下。
·maxRecordsPerBatch的配置不适用于组,并且由用户决定分组数据是否适合可用内存。
·以下示例显示如何使用groupby()。apply()从组中的每个值中减去均值。
from pyspark.sql.functions import pandas_udf, PandasUDFType df = spark.createDataFrame( [(1, 1.0), (1, 2.0), (2, 3.0), (2, 5.0), (2, 10.0)], ("id", "v")) @pandas_udf("id long, v double", PandasUDFType.GROUPED_MAP) def subtract_mean(pdf): # pdf is a pandas.DataFrame v = pdf.v return pdf.assign(v=v - v.mean()) df.groupby("id").apply(subtract_mean).show() # +---+----+ # | id| v| # +---+----+ # | 1|-0.5| # | 1| 0.5| # | 2|-3.0| # | 2|-1.0| # | 2| 4.0| # +---+----+
·在Spark repo中的“examples / src / main / python / sql / arrow.py”中找到完整的示例代码。
·有关详细用法,请参阅pyspark.sql.functions.pandas_udf和pyspark.sql.GroupedData.apply
Grouped Aggregate(分组聚合)
·分组聚合Pandas UDF类似于Spark聚合函数。
·分组聚合Pandas UDF与groupBy()。agg()和pyspark.sql.Window一起使用。
·它定义了从一个或多个pandas.Series到标量值的聚合,其中每个pandas.Series表示组或窗口中的列。
·请注意,此类型的UDF不支持部分聚合,组或窗口的所有数据都将加载到内存中。
·此外,目前只有Grouped聚合Pandas UDF支持无界窗口。
·以下示例显示如何使用此类型的UDF来计算groupBy和窗口操作的平均值:
from pyspark.sql.functions import pandas_udf, PandasUDFType from pyspark.sql import Window df = spark.createDataFrame( [(1, 1.0), (1, 2.0), (2, 3.0), (2, 5.0), (2, 10.0)], ("id", "v")) @pandas_udf("double", PandasUDFType.GROUPED_AGG) def mean_udf(v): return v.mean() df.groupby("id").agg(mean_udf(df['v'])).show() # +---+-----------+ # | id|mean_udf(v)| # +---+-----------+ # | 1| 1.5| # | 2| 6.0| # +---+-----------+ w = Window \ .partitionBy('id') \ .rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing) df.withColumn('mean_v', mean_udf(df['v']).over(w)).show() # +---+----+------+ # | id| v|mean_v| # +---+----+------+ # | 1| 1.0| 1.5| # | 1| 2.0| 1.5| # | 2| 3.0| 6.0| # | 2| 5.0| 6.0| # | 2|10.0| 6.0| # +---+----+------+
·在Spark repo中的“examples / src / main / python / sql / arrow.py”中找到完整的示例代码。
·有关详细用法,请参阅pyspark.sql.functions.pandas_udf
Usage Notes(使用说明)
Supported SQL Types(支持的SQL类型)
·目前,基于箭头的转换支持所有Spark SQL数据类型,但MapType,TimestampType的ArrayType和嵌套的StructType除外。
·仅当安装的PyArrow等于或高于0.10.0时才支持BinaryType。
Setting Arrow Batch Size(设置箭头批量大小)
·Spark中的数据分区将转换为箭头记录批次,这可能会暂时导致JVM中的高内存使用量。
·为避免可能的内存不足异常,可以通过将conf“spark.sql.execution.arrow.maxRecordsPerBatch”设置为一个整数来调整箭头记录批次的大小,该整数将确定每个批次的最大行数。
·默认值为每批10,000条记录。
·如果列数很大,则应相应地调整该值。
·使用此限制,每个数据分区将被制成一个或多个记录批次以进行处理。
Timestamp with Time Zone Semantics
·Spark在内部将时间戳存储为UTC值,并且在没有指定时区的情况下引入的时间戳数据将以本地时间转换为UTC,并具有微秒分辨率。
·在Spark中导出或显示时间戳数据时,会话时区用于本地化时间戳值。
·会话时区使用配置'spark.sql.session.timeZone'设置,如果未设置,将默认为JVM系统本地时区。
·Pandas使用具有纳秒分辨率的datetime64类型,datetime64 [ns],并且每列具有可选的时区。
·当时间戳数据从Spark传输到Pandas时,它将转换为纳秒,每列将转换为Spark会话时区,然后本地化到该时区,这将删除时区并将值显示为本地时间。
·使用timestamp列调用toPandas()或pandas_udf时会发生这种情况。
·当时间戳数据从Pandas传输到Spark时,它将转换为UTC微秒。
·当使用Pandas DataFrame调用createDataFrame或从pandas_udf返回时间戳时,会发生这种情况。
·这些转换是自动完成的,以确保Spark具有预期格式的数据,因此不必自己进行任何这些转换。
·任何纳秒值都将被截断。
·请注意,标准UDF(非Pandas)会将时间戳数据作为Python日期时间对象加载,这与Pandas时间戳不同。
·在pandas_udfs中使用时间戳时,建议使用Pandas时间序列功能以获得最佳性能,有关详细信息,请参阅此处。