Ydelsesfordele ved sorterede indekser
Sortér et MultiIndex med sort_index(), mål slice-ydeevne med timeit, og brug is_monotonic_increasing som kontrol.
Ydelsesfordele ved sorterede indekser er en gratis Pandas & NumPy Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Pandas & NumPy Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Pandas & NumPy Academy-kurset indeholder 4 lektioner i alt.
Hvorfor indekssortering er vigtig for ydeevnen
Et sorteret indeks gør det muligt for Pandas at bruge binær søgning (O(log n)) i stedet for en fuld lineær gennemgang (O(n)), når der slås op i etiketintervaller. For en DataFrame med en million rækker finder binær søgning målintervallet med cirka 20 sammenligninger mod op til en million sammenligninger ved en lineær gennemgang. Det gør udsnitsoperationer på sorterede MultiIndexes mange størrelsesordener hurtigere end på usorterede — og forskellen bliver afgørende i produktionspipelines, der behandler millioner af rækker.
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}')Kontrol af, om et indeks er sorteret
Brug df.index.is_monotonic_increasing til at kontrollere, om indekset er sorteret i stigende rækkefølge. Det returnerer en boolsk værdi. For et MultiIndex kontrollerer Pandas sorteringen leksikografisk på tværs af alle niveauer. Kontrollér altid dette, før du udfører udsnitsoperationer med .loc[start:end] på et MultiIndex — et usorteret indeks udløser enten UnsortedIndexError eller returnerer lydløst forkerte resultater, afhængigt af Pandas-versionen.
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)Sortering med sort_index()
df.sort_index() returnerer en ny DataFrame med rækker sorteret efter indeks etiketten i stigende rækkefølge. Brug ascending=False til faldende rækkefølge. For et MultiIndex er sorteringen leksikografisk: først sorteres der efter det yderste niveau, derefter efter de indre niveauer inden for hver ydre gruppe. Sortér altid efter operationer, der kan bringe indekset ud af orden — f.eks. pd.concat, filtrering eller tilføjelse af nye rækker.
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)Måling af opslagstid med timeit
Pythons modul timeit måler, hvor lang tid det tager at udføre en sætning, ved at køre den mange gange og beregne gennemsnittet. Brug det til at sammenligne opslag i sorterede og usorterede indekser. I IPython/Jupyter giver magien %timeit den samme funktionalitet med en mere overskuelig resultatvisning. Måling er den eneste pålidelige måde at bekræfte, at en optimering af ydeevnen faktisk hjalp — antag aldrig, at en ændring er hurtigere, uden at måle det.
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 fra usorteret MultiIndex
Pandas udsender en PerformanceWarning, når du laver et udsnit af et MultiIndex, der ikke er sorteret leksikografisk: 'indeksering efter lexsort-dybden kan påvirke ydeevnen'. Denne advarsel betyder, at Pandas måtte falde tilbage på en lineær gennemgang i stedet for binær søgning. Selvom den stadig returnerer korrekte resultater i simple tilfælde, kan den returnere forkerte resultater, når der laves udsnit af indre niveauer i et usorteret indeks med flere niveauer. Behandl denne advarsel som en fejl, og løs den egentlige årsag ved at sortere indekset.
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__)Sammenligning af udsnit af sorterede og usorterede MultiIndexes
Udsnit af et sorteret MultiIndex er markant hurtigere, fordi Pandas kan udføre binær søgning i både arrays for det ydre og det indre niveau. Lad os måle udsnit af et stort MultiIndex med 1 million rækker — en almindelig størrelse i produktionspipelines til analyse. Den sorterede version undgår den lineære gennemgang og viser konsekvent hastighedsforbedringer på 5-50× afhængigt af udsnittets selektivitet.
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 med parameteren level
For et MultiIndex kan du sortere efter et bestemt niveau i stedet for alle niveauer ved hjælp af parameteren level: df.sort_index(level='year'). Det er nyttigt, når du vil bevare grupperingen på det ydre niveau, men omorganisere rækkerne inden for hver ydre gruppe. Argumentet sort_remaining=True (standard) sorterer også eventuelle usorterede niveauer efter det angivne niveau og sikrer dermed fuld leksikografisk sortering.
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 som pipeline-vagt
Tilføj i produktionspipelines en sorteringsvagt i starten af enhver funktion, der modtager en DataFrame med et MultiIndex og udfører udsnit. Hvis indekset ikke er sorteret, skal du sortere det automatisk og skrive en advarsel i loggen. Det forhindrer en ubemærket forringelse af ydeevnen eller forkerte resultater, når kode opstrøms ændrer rækkefølgen i DataFrame. En vagt ved funktionsgrænsen er mere pålidelig end at antage, at kaldende kode altid sender sorterede data.
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)Sorteret indeks til binær søgning på simple indekser
Fordelene ved sortering gælder også for almindelige indekser (ikke-multiindekser). En DatetimeIndex, der bruges til tidsserieanalyse, udfører udsnit af datointervaller langt hurtigere, når det er sorteret. Et strengindeks, der er sorteret alfabetisk, gør binær søgning mulig ved opslag efter etiketter. For en stor Series med aktiekurser indekseret efter tidsstempler kan sortering af DatetimeIndex ændre et udsnit fra 100 ms til en operation på under et millisekund.
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')Hukommelsesomkostninger ved sortering
Sortering er ikke gratis — sort_index() opretter en ny kopi af DataFrame (medmindre du bruger inplace=True, som ændrer den på stedet). For meget store DataFrames fordobler det midlertidigt det maksimale hukommelsesforbrug. En pragmatisk strategi er at sortere én gang ved indlæsning og bevare den sorterede version gennem hele pipelinen i stedet for at sortere gentagne gange. Hvis hukommelsen er begrænset, kan du sortere på stedet med df.sort_index(inplace=True) for at undgå den midlertidige kopi.
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)Opsummering af bedste praksis for sorterede indekser
Vigtige regler for indeksydeevne: 1) Kald altid sort_index() efter enhver operation, der kan bringe indekset ud af orden (concat, merge, append, filter). 2) Brug is_monotonic_increasing som vagt i funktioner, der laver udsnit af indekset. 3) Sortér ved indlæsning, og bevar den sorterede DataFrame gennem hele pipelinen for at undgå gentagen sortering. 4) Sørg for, at alle niveauer i DataFrames med MultiIndex er sorterede, ikke kun det yderste. 5) Brug timeit til at kontrollere, at sortering faktisk giver den forventede hastighedsforbedring i netop din pipeline.
Hurtig kontrol
Afprøv din forståelse af ydeevnen for sorterede indekser fra denne lektion.
Opsummering af lektionen
I denne lektion lærte du, at is_monotonic_increasing kontrollerer, om et indeks er sorteret, og om binær søgning er mulig, at sort_index() sorterer på stedet eller returnerer en sorteret kopi, og at timeit måler den faktiske hastighedsforbedring for at bekræfte fordelen. Dernæst undersøger vi vinduesfunktioner — rullende og ekspanderende statistikker for tidsserier og finansielle data.
Lær Python med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 30
- Lektioner
- 120
Ofte stillede spørgsmål
Er lektionen “Ydelsesfordele ved sorterede indekser” gratis?
Ja — hele teksten til “Ydelsesfordele ved sorterede indekser” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Pandas & NumPy Academy-kurset, skal du opgradere til CoddyKit PRO. Pandas & NumPy Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Ydelsesfordele ved sorterede indekser”?
Sortér et MultiIndex med sort_index(), mål slice-ydeevne med timeit, og brug is_monotonic_increasing som kontrol. Du øver dig i Pandas & NumPy Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Pandas & NumPy Academy?
Der kræves ingen tidligere erfaring. Pandas & NumPy Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.
Hvor lang tid tager lektionen “Ydelsesfordele ved sorterede indekser”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Pandas & NumPy Academy-lektion?
Ja. Alle Pandas & NumPy Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Oprettelse af et MultiIndex
- Valg af data fra et MultiIndex
- Indekstilpasning og reindeksering
- Ydelsesfordele ved sorterede indekser