0Pricing
Pandas & NumPy Academy · Lección

Ventajas de rendimiento de los índices ordenados

Ordene un MultiIndex con sort_index(), mida el rendimiento de los segmentos con timeit y use is_monotonic_increasing como comprobación de seguridad.

Ventajas de rendimiento de los índices ordenados es una lección gratuita de Pandas & NumPy Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Pandas & NumPy Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

Por qué la ordenación de índices es importante para el rendimiento

Un índice ordenado permite que Pandas use una búsqueda binaria (O(log n)) en lugar de un recorrido lineal completo (O(n)) al buscar rangos de etiquetas. En un DataFrame con un millón de filas, la búsqueda binaria encuentra el rango objetivo con unas 20 comparaciones, frente a hasta un millón de comparaciones con un recorrido lineal. Esto hace que las operaciones de slicing sobre MultiIndexes ordenados sean varios órdenes de magnitud más rápidas que sobre índices sin ordenar, y la diferencia se vuelve crítica en pipelines de producción que procesan millones de filas.

import pandas as pd
import numpy as np

np.random.seed(42)
# Create a large DataFrame with a MultiIndex
countries = ['DE', 'UK', 'USA', 'FR', 'JP']
dates = pd.date_range('2020-01-01', periods=200)
mi = pd.MultiIndex.from_product([countries, dates], names=['country', 'date'])
df = pd.DataFrame({'value': np.random.randn(len(mi))}, index=mi)

print(f'DataFrame shape: {df.shape}')
print(f'Index is sorted: {df.index.is_monotonic_increasing}')

Comprobar si un índice está ordenado

Use df.index.is_monotonic_increasing para comprobar si el índice está ordenado en orden ascendente. Devuelve un booleano. En un MultiIndex, Pandas comprueba la ordenación lexicográfica en todos los niveles. Compruébelo siempre antes de realizar operaciones de slicing con .loc[start:end] en un MultiIndex: un índice sin ordenar generará UnsortedIndexError o devolverá silenciosamente resultados incorrectos, según la versión de Pandas.

import pandas as pd

# Sorted MultiIndex
tuples_sorted = [('A', 1), ('A', 2), ('B', 1), ('B', 2)]
mi_sorted = pd.MultiIndex.from_tuples(tuples_sorted)
df_sorted = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi_sorted)

# Unsorted MultiIndex
tuples_unsorted = [('B', 2), ('A', 1), ('B', 1), ('A', 2)]
mi_unsorted = pd.MultiIndex.from_tuples(tuples_unsorted)
df_unsorted = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi_unsorted)

print('Sorted index is_monotonic_increasing:', df_sorted.index.is_monotonic_increasing)
print('Unsorted index is_monotonic_increasing:', df_unsorted.index.is_monotonic_increasing)

Ordenar con sort_index()

df.sort_index() devuelve un DataFrame nuevo con las filas ordenadas por etiqueta de índice en orden ascendente. Use ascending=False para ordenarlas de forma descendente. En un MultiIndex, la ordenación es lexicográfica: primero ordena por el nivel más externo y después por los niveles internos dentro de cada grupo externo. Ordene siempre después de cualquier operación que pueda desordenar el índice, como pd.concat, el filtrado o la adición de nuevas filas.

import pandas as pd
import numpy as np

np.random.seed(0)
countries = ['USA', 'UK', 'DE']
years = [2021, 2022, 2023]
mi = pd.MultiIndex.from_product([countries, years], names=['country', 'year'])
df = pd.DataFrame({'gdp': np.random.randint(3000, 26000, 9)}, index=mi)

print('Before sort_index():')
print(df.head(4))

df_sorted = df.sort_index()
print('\nAfter sort_index():')
print(df_sorted.head(4))
print('Is sorted:', df_sorted.index.is_monotonic_increasing)

Medir el tiempo de búsqueda con timeit

El módulo timeit de Python mide cuánto tarda en ejecutarse una instrucción ejecutándola muchas veces y calculando el promedio. Úselo para comparar el rendimiento de las búsquedas en índices ordenados y sin ordenar. En IPython/Jupyter, la magia %timeit ofrece la misma funcionalidad con una salida más clara. La evaluación comparativa es la única forma fiable de confirmar que una optimización del rendimiento realmente ha ayudado: nunca dé por hecho que un cambio es más rápido sin medirlo.

import pandas as pd
import numpy as np
import timeit

np.random.seed(0)
N = 500000
idx = np.random.choice(['A','B','C','D','E'], N)
df_unsorted = pd.DataFrame({'v': np.random.randn(N)}, index=idx)
df_sorted = df_unsorted.sort_index()

# Time label lookup: sorted vs unsorted
t_unsorted = timeit.timeit(lambda: df_unsorted.loc['C'], number=100)
t_sorted = timeit.timeit(lambda: df_sorted.loc['C'], number=100)

print(f'Unsorted lookup (100 runs): {t_unsorted:.3f}s')
print(f'Sorted lookup  (100 runs): {t_sorted:.3f}s')
print(f'Speedup: {t_unsorted/t_sorted:.1f}x')

PerformanceWarning de un MultiIndex sin ordenar

Pandas emite un PerformanceWarning al realizar slicing sobre un MultiIndex que no está ordenado lexicográficamente: 'la indexación más allá de la profundidad de lexsort puede afectar al rendimiento'. Esta advertencia significa que Pandas tuvo que recurrir a un recorrido lineal en lugar de una búsqueda binaria. Aunque en casos sencillos sigue devolviendo resultados correctos, puede devolver resultados incorrectos al hacer slicing sobre niveles internos de un índice multinivel sin ordenar. Trate esta advertencia como un error y corrija la causa subyacente ordenando el índice.

import pandas as pd
import warnings

# Create an unsorted MultiIndex and trigger the warning
tuples = [('B', 2), ('A', 1), ('B', 1), ('A', 2)]
mi = pd.MultiIndex.from_tuples(tuples, names=['letter', 'num'])
df = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi)

print('Index sorted?', df.index.is_monotonic_increasing)

# This may trigger PerformanceWarning in some Pandas versions
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter('always')
    try:
        result = df.loc['A':'B', :]
        print('Result:', result)
        if w:
            print('Warning:', str(w[0].message))
    except Exception as e:
        print('Error (common with newer Pandas):', type(e).__name__)

Evaluación comparativa del slicing de MultiIndex ordenados y sin ordenar

El slicing de un MultiIndex ordenado es mucho más rápido porque Pandas puede realizar búsquedas binarias tanto en los arrays de los niveles externos como en los internos. Compare el rendimiento del slicing de un MultiIndex grande con un millón de filas, un tamaño habitual en los pipelines de análisis de producción. La versión ordenada evita el recorrido lineal y muestra de forma constante mejoras de entre 5 y 50 veces, según la selectividad del slicing.

import pandas as pd
import numpy as np
import timeit

np.random.seed(42)
countries = ['DE', 'UK', 'USA', 'FR', 'JP']
dates = pd.date_range('2010-01-01', periods=200000)

# Sample a random subset for timing test
sample_countries = np.random.choice(countries, 100000)
sample_dates = np.random.choice(dates, 100000)

df = pd.DataFrame({
    'country': sample_countries,
    'date': sample_dates,
    'value': np.random.randn(100000)
}).set_index(['country', 'date'])

df_sorted = df.sort_index()
print('Dataset size:', len(df))
print('Sorted:', df_sorted.index.is_monotonic_increasing)

t = timeit.timeit(lambda: df_sorted.loc['USA'], number=50)
print(f'Sorted lookup (50 runs): {t:.3f}s')

sort_index con el parámetro level

En un MultiIndex, puede ordenar por un nivel específico en lugar de por todos los niveles mediante el parámetro level: df.sort_index(level='year'). Esto resulta útil cuando desea conservar la agrupación del nivel externo, pero reordenar las filas dentro de cada grupo externo. El argumento sort_remaining=True (predeterminado) también ordena los niveles no ordenados posteriores al nivel especificado, lo que garantiza una ordenación lexicográfica completa.

import pandas as pd
import numpy as np

countries = ['USA', 'UK']
years = [2023, 2021, 2022]  # deliberately unordered
mi = pd.MultiIndex.from_product([countries, years], names=['country', 'year'])
df = pd.DataFrame({'v': range(6)}, index=mi)

print('Before sorting by year level:')
print(df)

# Sort by the inner level (year) only
df_ysorted = df.sort_index(level='year')
print('\nAfter sort_index(level="year"):')
print(df_ysorted)

is_monotonic_increasing como protección del pipeline

En los pipelines de producción, añada una protección de ordenación al inicio de cualquier función que reciba un DataFrame con un MultiIndex y realice slicing. Si el índice no está ordenado, ordénelo automáticamente y registre una advertencia. Esto evita una degradación silenciosa del rendimiento o resultados incorrectos cuando el código ascendente cambia el orden del DataFrame. Una protección en el límite de la función es más fiable que suponer que los llamadores siempre proporcionan datos ordenados.

import pandas as pd
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def safe_slice(df, key):
    '''Slice a MultiIndex DataFrame, sorting if necessary.'''
    if not df.index.is_monotonic_increasing:
        logger.warning('Index not sorted — sorting now. This is a performance cost.')
        df = df.sort_index()
    return df.loc[key]

# Test with an unsorted DataFrame
tuples = [('B', 2), ('A', 1), ('B', 1), ('A', 2)]
mi = pd.MultiIndex.from_tuples(tuples, names=['letter', 'num'])
df = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi)

result = safe_slice(df, 'A')
print('Slice result for A:')
print(result)

Índice ordenado para búsquedas binarias en índices simples

Las ventajas de rendimiento de la ordenación también se aplican a los índices normales (no multinivel). Un DatetimeIndex utilizado en análisis de series temporales realiza el slicing de rangos de fechas mucho más rápido cuando está ordenado. Un índice de cadenas ordenado alfabéticamente permite realizar búsquedas binarias de etiquetas. En una Series grande de precios de acciones indexada por marca de tiempo, ordenar el DatetimeIndex puede convertir un slicing de 100 ms en una operación de menos de un milisegundo.

import pandas as pd
import numpy as np
import timeit

np.random.seed(0)
# Random timestamps — unsorted
timestamps = pd.date_range('2020-01-01', periods=500000, freq='min')
shuffled = np.random.permutation(timestamps)

prices = pd.Series(np.random.randn(500000), index=shuffled)
prices_sorted = prices.sort_index()

# Time a date range slice
t_unsorted = timeit.timeit(lambda: prices['2020-06-01':'2020-06-30'], number=20)
t_sorted = timeit.timeit(lambda: prices_sorted['2020-06-01':'2020-06-30'], number=20)

print(f'Unsorted: {t_unsorted:.3f}s')
print(f'Sorted:   {t_sorted:.3f}s')
print(f'Speedup: {t_unsorted/t_sorted:.0f}x')

Coste de memoria de la ordenación

Ordenar no es gratis: sort_index() crea una copia nueva del DataFrame (a menos que use inplace=True, que modifica el objeto en el sitio). En DataFrames muy grandes, esto duplica temporalmente el uso máximo de memoria. Una estrategia práctica consiste en ordenar una vez al cargar los datos y conservar la versión ordenada durante todo el pipeline, en lugar de ordenar repetidamente. Si la memoria es limitada, ordene en el sitio con df.sort_index(inplace=True) para evitar la copia temporal.

import pandas as pd
import numpy as np

np.random.seed(0)
countries = ['DE', 'UK', 'USA']
years = [2021, 2022, 2023]
mi = pd.MultiIndex.from_product([countries, years], names=['country', 'year'])
df = pd.DataFrame({'v': np.random.randn(9)}, index=mi)

# Sort once at load time — best practice
df.sort_index(inplace=True)  # no temporary copy
assert df.index.is_monotonic_increasing, 'Index must be sorted!'
print('Pipeline-ready DataFrame (sorted in place):')
print(df)

Resumen de buenas prácticas para índices ordenados

Reglas clave para el rendimiento de los índices: 1) Llame siempre a sort_index() después de cualquier operación que pueda desordenar el índice (concat, merge, append, filter). 2) Use is_monotonic_increasing como protección en las funciones que realizan slicing del índice. 3) Ordene al cargar los datos y conserve el DataFrame ordenado durante todo el pipeline para evitar ordenaciones repetidas. 4) En los DataFrames con MultiIndex, asegúrese de que estén ordenados todos los niveles, no solo el más externo. 5) Use timeit para verificar que la ordenación proporciona realmente la mejora de velocidad esperada en su pipeline concreto.

Comprobación rápida

Compruebe su comprensión del rendimiento de los índices ordenados explicado en esta lección.

Resumen de la lección

En esta lección ha aprendido que is_monotonic_increasing comprueba si un índice está ordenado y si es posible utilizar una búsqueda binaria; sort_index() ordena en el sitio o devuelve una copia ordenada; y timeit mide la mejora de velocidad real para confirmar el beneficio. A continuación, exploraremos las funciones de ventana: estadísticas móviles y expansivas para series temporales y datos financieros.

Preguntas frecuentes

¿La lección «Ventajas de rendimiento de los índices ordenados» es gratis?

Sí — el texto completo de «Ventajas de rendimiento de los índices ordenados» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Pandas & NumPy Academy, actualiza a CoddyKit PRO. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Ventajas de rendimiento de los índices ordenados»?

Ordene un MultiIndex con sort_index(), mida el rendimiento de los segmentos con timeit y use is_monotonic_increasing como comprobación de seguridad. Practicas Pandas & NumPy Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Pandas & NumPy Academy?

No se requiere experiencia previa. Pandas & NumPy Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Ventajas de rendimiento de los índices ordenados»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Pandas & NumPy Academy?

Sí. Cada lección de Pandas & NumPy Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Crear un MultiIndex
  2. Seleccionar datos de un MultiIndex
  3. Alineación y reindexación de índices
  4. Ventajas de rendimiento de los índices ordenados
← Volver a Pandas & NumPy Academy