0Pricing
Pandas & NumPy Academy · Lektion

Leistungsvorteile sortierter Indizes

Sortieren Sie einen MultiIndex mit sort_index(), messen Sie die Slice-Leistung mit timeit und verwenden Sie is_monotonic_increasing als Schutzprüfung.

Leistungsvorteile sortierter Indizes ist eine kostenlose Pandas & NumPy Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Pandas & NumPy Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Pandas & NumPy Academy-Kurs umfasst insgesamt 4 Lektionen.

Warum die Indexsortierung für die Performance wichtig ist

Ein sortierter Index ermöglicht es Pandas, bei der Suche nach Bezeichnungsbereichen eine binäre Suche (O(log n)) statt eines vollständigen linearen Durchlaufs (O(n)) zu verwenden. Bei einem DataFrame mit einer Million Zeilen findet die binäre Suche den Zielbereich mit etwa 20 Vergleichen, während ein linearer Durchlauf bis zu eine Million Vergleiche benötigt. Dadurch sind Slice-Operationen auf sortierten MultiIndexes um Größenordnungen schneller als auf unsortierten – und der Unterschied wird in Produktionspipelines, die Millionen von Zeilen verarbeiten, entscheidend.

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}')

Prüfen, ob ein Index sortiert ist

Verwenden Sie df.index.is_monotonic_increasing, um zu prüfen, ob der Index aufsteigend sortiert ist. Das Ergebnis ist ein boolescher Wert. Bei einem MultiIndex prüft Pandas die Sortierung lexikografisch über alle Ebenen. Prüfen Sie dies immer, bevor Sie Slice-Operationen mit .loc[start:end] auf einem MultiIndex ausführen – ein unsortierter Index löst je nach Pandas-Version entweder einen UnsortedIndexError aus oder liefert stillschweigend falsche Ergebnisse.

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)

Sortieren mit sort_index()

df.sort_index() gibt einen neuen DataFrame zurück, dessen Zeilen nach der Indexbezeichnung aufsteigend sortiert sind. Verwenden Sie ascending=False für eine absteigende Sortierung. Bei einem MultiIndex erfolgt die Sortierung lexikografisch: Zuerst wird nach der äußersten Ebene sortiert, anschließend innerhalb jeder äußeren Gruppe nach den inneren Ebenen. Sortieren Sie immer nach jeder Operation, die den Index möglicherweise durcheinanderbringt – etwa nach pd.concat, dem Filtern oder dem Anhängen neuer Zeilen.

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)

Nachschlagezeit mit timeit messen

Das Python-Modul timeit misst, wie lange die Ausführung einer Anweisung dauert, indem es sie viele Male ausführt und den Durchschnitt bildet. Verwenden Sie es, um Nachschlagen in sortierten und unsortierten Indizes zu vergleichen. In IPython/Jupyter bietet das Magic-Kommando %timeit dieselbe Funktionalität mit einer übersichtlicheren Ausgabe. Benchmarking ist die einzige zuverlässige Methode, um zu bestätigen, dass eine Performanceoptimierung tatsächlich geholfen hat – nehmen Sie nie ohne Messung an, dass eine Änderung schneller ist.

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 bei unsortiertem MultiIndex

Pandas gibt eine PerformanceWarning aus, wenn Sie einen MultiIndex slicen, der nicht lexikografisch sortiert ist: 'indexing past lexsort depth may impact performance'. Diese Warnung bedeutet, dass Pandas statt einer binären Suche auf einen linearen Durchlauf zurückgreifen musste. In einfachen Fällen werden weiterhin korrekte Ergebnisse zurückgegeben. Beim Slicen innerer Ebenen eines unsortierten mehrstufigen Index kann Pandas jedoch falsche Ergebnisse liefern. Behandeln Sie diese Warnung wie einen Fehler und beheben Sie die Ursache, indem Sie den Index sortieren.

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__)

Benchmarking von Slices in sortierten und unsortierten MultiIndexes

Das Slicing eines sortierten MultiIndex ist deutlich schneller, weil Pandas sowohl in den Arrays der äußeren als auch der inneren Ebene eine binäre Suche durchführen kann. Wir messen die Zeit für das Slicing eines großen MultiIndex mit 1 Million Zeilen – einer in Produktionspipelines für Analysen häufigen Größe. Die sortierte Variante vermeidet den linearen Durchlauf und erzielt je nach Selektivität des Slices durchgängig eine 5- bis 50-fache Beschleunigung.

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 mit dem Parameter level

Bei einem MultiIndex können Sie mit dem Parameter level nach einer bestimmten Ebene statt nach allen Ebenen sortieren: df.sort_index(level='year'). Das ist nützlich, wenn Sie die Gruppierung nach der äußeren Ebene beibehalten, die Zeilen innerhalb jeder äußeren Gruppe jedoch neu anordnen möchten. Das Argument sort_remaining=True (Standardwert) sortiert außerdem alle unsortierten Ebenen hinter der angegebenen Ebene und stellt so eine vollständige lexikografische Sortierung sicher.

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 als Pipeline-Schutz

Fügen Sie in Produktionspipelines am Anfang jeder Funktion, die einen DataFrame mit einem MultiIndex erhält und Slicing durchführt, einen Sortierungsschutz ein. Wenn der Index nicht sortiert ist, sortieren Sie ihn automatisch und protokollieren Sie eine Warnung. So verhindern Sie eine unbemerkte Verschlechterung der Performance oder falsche Ergebnisse, wenn vorgelagerter Code die Reihenfolge des DataFrame ändert. Ein Schutz an der Funktionsgrenze ist zuverlässiger, als davon auszugehen, dass Aufrufer immer sortierte Daten übergeben.

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)

Sortierter Index für die binäre Suche bei einfachen Indizes

Die Performancevorteile der Sortierung gelten auch für reguläre Indizes (keine MultiIndexes). Ein DatetimeIndex, der in Zeitreihenanalysen verwendet wird, ermöglicht bei sortierten Daten ein deutlich schnelleres Slicing von Datumsbereichen. Ein alphabetisch sortierter String-Index erlaubt die binäre Suche nach Bezeichnungen. Bei einer großen Series mit Zeitstempeln als Index kann das Sortieren des DatetimeIndex ein Slice von 100 ms auf weniger als eine Millisekunde beschleunigen.

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')

Speicherkosten der Sortierung

Sortieren ist nicht kostenlos – sort_index() erstellt eine neue Kopie des DataFrame (außer bei Verwendung von inplace=True, das die Änderung direkt am bestehenden Objekt vornimmt). Bei sehr großen DataFrames verdoppelt sich dadurch vorübergehend der maximale Speicherbedarf. Eine pragmatische Strategie besteht darin, einmal beim Laden zu sortieren und die sortierte Version in der gesamten Pipeline beizubehalten, statt wiederholt zu sortieren. Wenn der Speicher knapp ist, sortieren Sie direkt mit df.sort_index(inplace=True), um die temporäre Kopie zu vermeiden.

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)

Zusammenfassung der Best Practices für sortierte Indizes

Wichtige Regeln für die Index-Performance: 1) Rufen Sie nach jeder Operation, die den Index möglicherweise durcheinanderbringt (concat, merge, append, filter), sort_index() auf. 2) Verwenden Sie is_monotonic_increasing als Schutz in Funktionen, die den Index slicen. 3) Sortieren Sie beim Laden und behalten Sie den sortierten DataFrame in der gesamten Pipeline bei, um wiederholtes Sortieren zu vermeiden. 4) Stellen Sie bei MultiIndex-DataFrames sicher, dass alle Ebenen sortiert sind, nicht nur die äußerste. 5) Verwenden Sie timeit, um zu überprüfen, ob die Sortierung in Ihrer konkreten Pipeline tatsächlich die erwartete Beschleunigung bringt.

Kurzer Test

Testen Sie Ihr Verständnis der Performance sortierter Indizes aus dieser Lektion.

Lektionszusammenfassung

In dieser Lektion haben Sie gelernt: is_monotonic_increasing prüft, ob ein Index sortiert ist und eine binäre Suche verwendet werden kann, sort_index() sortiert direkt oder gibt eine sortierte Kopie zurück, und timeit misst die tatsächliche Beschleunigung, um den Vorteil zu bestätigen. Als Nächstes untersuchen wir Window-Funktionen – gleitende und expandierende Statistiken für Zeitreihen und Finanzdaten.

Häufig gestellte Fragen

Ist die Lektion „Leistungsvorteile sortierter Indizes“ kostenlos?

Ja — der vollständige Text von „Leistungsvorteile sortierter Indizes“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Pandas & NumPy Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Pandas & NumPy Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Leistungsvorteile sortierter Indizes“?

Sortieren Sie einen MultiIndex mit sort_index(), messen Sie die Slice-Leistung mit timeit und verwenden Sie is_monotonic_increasing als Schutzprüfung. Du übst Pandas & NumPy Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Pandas & NumPy Academy zu starten?

Keine Vorkenntnisse erforderlich. Pandas & NumPy Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Leistungsvorteile sortierter Indizes“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Pandas & NumPy Academy-Lektion Code schreiben und ausführen?

Ja. Jede Pandas & NumPy Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Einen MultiIndex erstellen
  2. Daten aus einem MultiIndex auswählen
  3. Indexausrichtung und Reindexierung
  4. Leistungsvorteile sortierter Indizes
← Zurück zu Pandas & NumPy Academy