Todos los productos
Search
Centro de documentos

MaxCompute:Mejores prácticas para usar el operador apply_chunk de MaxFrame

Última actualización: Sep 03, 2026

Al ejecutar df.apply() en un DataFrame distribuido de MaxFrame, los datos se procesan fila por fila, lo que genera un cuello de botella a gran escala. apply_chunk resuelve este problema al procesar los datos en lotes configurables y enviar cada lote a un worker de MaxCompute como un DataFrame de pandas. Para cargas de trabajo sensibles al rendimiento, utilice apply_chunk en lugar de df.apply().

Utilice apply_chunk cuando:

  • Su función definida por el usuario (UDF) opera sobre un DataFrame de pandas y necesita ejecutarse a gran escala.

  • Desea controlar el uso de memoria y el grado de paralelismo.

  • El procesamiento fila por fila con df.apply() es demasiado lento para su volumen de datos.

Funcionamiento

apply_chunk divide el DataFrame distribuido en fragmentos, envía cada fragmento a un worker de MaxCompute como un DataFrame de pandas y combina los resultados. Los parámetros clave —batch_rows, output_type y dtypes— indican a MaxFrame cómo particionar los datos, qué devuelve la UDF y cómo validar y combinar la salida. También puede usar decoradores de UDF como @with_python_requirements para gestionar dependencias en tareas complejas.

Parámetros

DataFrame.mf.apply_chunk(
    func,
    batch_rows=None,
    output_type=None,
    dtypes=None,
    index=None,
    index_value=None,
    columns=None,
    elementwise=None,
    sort=False,
    **kwds
)

Parameter

Type

Description

func

callable

Una UDF que acepta un DataFrame de pandas (un fragmento de filas) y devuelve un DataFrame o una Serie de pandas.

batch_rows

int

Número máximo de filas por fragmento. Controla el uso de memoria y el grado de paralelismo.

output_type

str

Tipo de salida: "dataframe" o "series".

dtypes

pd.Series

Tipos de datos de las columnas de salida. Deben coincidir exactamente con las columnas que devuelve func.

index

Index

Objeto de índice de salida.

index_value

IndexValue

Metadatos para el índice distribuido. Obténgalos del DataFrame original.

sort

bool

Indica si se deben ordenar los datos dentro de los grupos en un escenario de groupby.

Descripción de dtypes

dtypes es un objeto pd.Series que describe los nombres de columna y los tipos de datos de la salida de su UDF. MaxFrame lo utiliza para validar y combinar resultados entre workers. Si dtypes no coincide con lo que realmente devuelve func, el trabajo falla durante la ejecución.

Para inspeccionar la estructura de dtypes en un DataFrame:

>>> df.dtypes
A    object
B    object
dtype: object

La forma más común de proporcionar dtypes es copiarlo del DataFrame original:

dtypes=df.dtypes.copy()
Importante

Nunca pase df.dtypes directamente. Utilice siempre .copy() para evitar modificar los metadatos del DataFrame original.

Si su UDF modifica el esquema de salida (agrega o elimina columnas), construya un nuevo objeto pd.Series que describa la salida real:

import pandas as pd

# UDF drops column 'A' and adds column 'C'
new_dtypes = pd.Series({
    "B": df.dtypes["B"],
    "C": pd.StringDtype(),
})

Ejemplo

El siguiente ejemplo crea un DataFrame de MaxFrame con una columna de tipo diccionario y luego usa apply_chunk para actualizar valores en cada fragmento.

import os
import pyarrow as pa
import pandas as pd
import maxframe.dataframe as md
from maxframe.lib.dtypes_extension import dict_
from maxframe import new_session
from odps import ODPS

o = ODPS(
    # Get credentials from environment variables.
    # Never hardcode AccessKey ID or AccessKey secret in your code.
    os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
    os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
    project='<your-project>',
    endpoint='https://service.cn-<your-region>.maxcompute.aliyun.com/api',
)

session = new_session(o)

# Create a MaxFrame DataFrame with a dict-type column
col_a = pd.Series(
    data=[[("k1", 1), ("k2", 2)], [("k1", 3)], None],
    index=[1, 2, 3],
    dtype=dict_(pa.string(), pa.int64()),
)
col_b = pd.Series(
    data=["A", "B", "C"],
    index=[1, 2, 3],
)
df = md.DataFrame({"A": col_a, "B": col_b})
df.execute()

def custom_set_item(df: pd.DataFrame) -> pd.DataFrame:
    """Add key 'x' with value 100 to each non-null dict in column A."""
    for name, value in df["A"].items():
        if value is not None:
            df["A"][name]["x"] = 100
    return df

result_df = df.mf.apply_chunk(
    custom_set_item,
    output_type="dataframe",
    dtypes=df.dtypes.copy(),   # Must match the columns returned by the UDF
    batch_rows=2,              # Process 2 rows per chunk
    skip_infer=True,
    index=df.index,
).execute()

session.destroy()

Reemplace los siguientes marcadores de posición con los valores reales:

Placeholder

Description

Example

<your-project>

Nombre del proyecto de MaxCompute

my_project

<your-region>

ID de región

cn-hangzhou

Ajuste de rendimiento

Establezca batch_rows según sus datos y recursos

batch_rows controla cuántas filas procesa cada worker por fragmento. El valor adecuado depende del tamaño de sus filas y de la memoria disponible:

Direction

Effect

Risk

Valores mayores

Menos fragmentos, menor sobrecarga de programación, mejor throughput

Errores de falta de memoria (OOM) si las filas son anchas o la UDF consume mucha memoria

Valores menores

Más fragmentos, mayor grado de paralelismo

Mayor sobrecarga de programación para fragmentos muy pequeños

Comience con un valor conservador, supervise el uso de memoria en LogView y ajústelo según sea necesario. Si observa errores OOM, reduzca batch_rows. Si los trabajos se ejecutan rápidamente con bajo uso de memoria, auméntelo.

Si su UDF requiere más memoria por fragmento, use el decorador @with_running_options para aumentar el límite de memoria por tarea:

from maxframe.udf import with_running_options

@with_running_options(memory=16)
def my_udf(df: pd.DataFrame) -> pd.DataFrame:
    ...

Declare siempre output_type y dtypes explícitamente

De forma predeterminada, MaxFrame intenta inferir el esquema de salida ejecutando su función sobre datos de muestra. La inferencia puede fallar o producir resultados incorrectos en esquemas complejos. Declarar explícitamente output_type y dtypes evita fallos en tiempo de ejecución y acelera el trabajo.

Sin declaración explícita (falla en tiempo de ejecución):

result_df = df.mf.apply_chunk(process)  # dtypes is missing

Con declaración explícita (correcto):

result_df = df.mf.apply_chunk(
    process,
    output_type="dataframe",
    dtypes=df.dtypes.copy(),
)

Un error común es pasar dtypes que no coinciden con la salida real de la UDF. Por ejemplo, si elimina una columna dentro de func pero sigue pasando el df.dtypes original, MaxFrame genera un ValueError porque el esquema declarado tiene más columnas que el DataFrame devuelto.

Devuelva solo las columnas necesarias

Cada columna de salida se serializa, transfiere y combina entre workers. Devolver columnas no utilizadas aumenta el uso de memoria y ralentiza el trabajo. Dentro de func, elimine cualquier columna que no necesite antes de devolver el resultado.

Depure UDF con print y flush=True

Las UDF se ejecutan en workers remotos de MaxCompute. Use print(..., flush=True) para emitir registros de inmediato; estos aparecerán en LogView. Envuelva el cuerpo de la UDF en un bloque try/except para capturar y registrar errores antes de que se manifiesten como fallos genéricos:

def process(chunk: pd.DataFrame) -> pd.DataFrame:
    try:
        print(f"Processing chunk: shape={chunk.shape}, columns={list(chunk.columns)}", flush=True)
        result = chunk.sort_values("B")
        print("Chunk processed successfully.", flush=True)
        return result
    except Exception as e:
        print(f"[ERROR] {type(e).__name__}: {e}", flush=True)
        raise

Preguntas frecuentes

¿Por qué obtengo TypeError: cannot determine dtype?

MaxFrame no pudo inferir el esquema de salida. Pase dtypes y output_type explícitamente al llamar a apply_chunk.

¿Por qué mi salida está vacía o le faltan columnas?

Los dtypes que pasó no coinciden con lo que devuelve su UDF. Compruebe que los nombres de columna en dtypes coincidan exactamente con las columnas del DataFrame que devuelve func. Elimine de dtypes cualquier columna que func no incluya en su valor de retorno.

¿Por qué el trabajo se cuelga o supera el tiempo de espera?

Probablemente batch_rows sea demasiado grande. Redúzcalo y asigne más recursos para que cada worker procese un fragmento más pequeño y rápido. Revise también LogView en busca de errores OOM, ya que a menudo se manifiestan como cuelgues en lugar de fallos explícitos.