O que são funções definidas pelo usuário (UDFs)?

Uma função definida pelo usuário (UDF) é uma função definida por um usuário, permitindo que a lógica personalizada seja reutilizada no ambiente do usuário. Databricks tem suporte para muitos tipos diferentes de UDFs para permitir a distribuição de lógica extensível. Este artigo apresenta alguns dos pontos fortes e limitações gerais das UDFs.

Observação

Nem todas as formas de UDFs estão disponíveis em todos os ambientes de execução no Databricks. Se você estiver trabalhando com Unity Catalog, consulte Funções definidas pelo usuário (UDFs) no Unity Catalog.

Consulte os seguintes artigos para obter mais informações sobre UDFs:

Definindo a lógica personalizada sem penalidades de serialização

Databricks herda muito de seus comportamentos UDF do Apache Spark, incluindo as limitações de eficiência em torno de muitos tipos de UDFs. Consulte Quais UDFs são mais eficientes?.

Você pode modularizar seu código com segurança sem se preocupar com potenciais compensações de eficiência associadas a UDFs. Para fazer isso, você deve definir sua lógica como uma série de métodos integrados do Spark usando SQL ou Spark DataFrames. Por exemplo, as seguintes funções SQL e Python combinam métodos integrados do Spark para definir uma conversão de unidade como uma função reutilizável:

CREATE FUNCTION convert_f_to_c(unit STRING, temp DOUBLE)
RETURNS DOUBLE
RETURN CASE
  WHEN unit = "F" THEN (temp - 32) * (5/9)
  ELSE temp
END;

SELECT convert_f_to_c(unit, temp) AS c_temp
FROM tv_temp;
def convertFtoC(unitCol, tempCol):
  from pyspark.sql.functions import when
  return when(unitCol == "F", (tempCol - 32) * (5/9)).otherwise(tempCol)

from pyspark.sql.functions import col

df_query = df.select(convertFtoC(col("unit"), col("temp"))).toDF("c_temp")
display(df_query)

Para executar as UDFs acima, você pode criar dados de exemplo.

Quais UDFs são mais eficientes?

UDFs podem introduzir gargalos de processamento significativos na execução do código. O Databricks usa vários otimizadores diferentes automaticamente para código escrito com a sintaxe Apache Spark, SQL e Delta Lake incluída. Quando a lógica personalizada é introduzida por UDFs, esses otimizadores não têm a capacidade de planejar tarefas com eficiência em torno dessa lógica personalizada. Além disso, a lógica executada fora da JVM tem custos adicionais relacionados à serialização de dados.

Observação

O Databricks otimiza muitas funções usando o Photon se você usar compute habilitada para Photon. Somente funções que encadeiam comandos Spark SQL de DataFrame podem ser otimizadas pelo Photon.

Algumas UDFs são mais eficientes do que outras. Em termos de desempenho:

  • As funções integradas serão mais rápidas por causa dos otimizadores do Databricks.

  • O código executado na JVM (Scala, Java, Hive UDFs) será mais rápido que os UDFs do Python.

  • Os UDFs do Pandas usam o Arrow para reduzir os custos de serialização associados aos UDFs do Python.

  • Os UDFs do Python funcionam bem para lógica processual, mas devem ser evitados para cargas de trabalho ETL de produção em grandes dataset.

Observação

No Databricks Runtime 14.0 e abaixo, os UDFs escalares do Python e os UDFs do Pandas não têm suporte no Unity Catalog em clusters que usam o modo de acesso compartilhado. Estes UDFs são suportados para todos os modos de acesso no Databricks Runtime 14.1 e acima.

No Databricks Runtime 14.1 e abaixo, os UDFs escalares Scala não têm suporte no Unity Catalog em clusters que usam o modo de acesso compartilhado. Estes UDFs são suportados para todos os modos de acesso no Databricks Runtime 14.2 e acima.

No Databricks Runtime 14.1 e acima, você pode registrar UDFs escalares do Python no Unity Catalog usando a sintaxe SQL. Consulte Funções definidas pelo usuário (UDFs) no Unity Catalog.

Tipo

Otimizado

Ambiente de execução

Hive UDF

Não

JVMName

Python UDFName

Não

Python

Pandas UDF

Não

Python (seta)

Scala UDF

Não

JVMName

Spark SQL

Sim

JVM (Photon)

Spark DataFrame

Sim

JVM (Photon)

Quando você deve usar um UDF?

Um grande benefício dos UDFs é que eles permitem que os usuários expressem a lógica em linguagens familiares, reduzindo o custo humano associado à refatoração do código. Para query ad hoc, limpeza manual de dados, análise exploratória de dados e a maioria das operações em dataset pequenos ou médios, é improvável que os custos indiretos de latência associados a UDFs superem os custos associados à refatoração do código.

Para ETL Job, operações de transmissão, operações em dataset muito grandes ou outras cargas de trabalho executadas regularmente ou continuamente, refatorar a lógica para usar métodos nativos do Apache Spark rende dividendos rapidamente.

Dados de exemplo para UDFs de exemplo

Os exemplos de código neste artigo usam UDFs para converter temperaturas entre Celsius e Farenheit. Se você deseja executar essas funções, pode criar um dataset de amostra com o seguinte código Python:

import numpy as np
import pandas as pd

Fdf = pd.DataFrame(np.random.normal(55, 25, 10000000), columns=["temp"])
Fdf["unit"] = "F"

Cdf = pd.DataFrame(np.random.normal(10, 10, 10000000), columns=["temp"])
Cdf["unit"] = "C"

df = spark.createDataFrame(pd.concat([Fdf, Cdf]).sample(frac=1))

df.cache().count()
df.createOrReplaceTempView("tv_temp")