Home Nieuws Waarom je moet stoppen met het schrijven van loops in panda’s

Waarom je moet stoppen met het schrijven van loops in panda’s

3
0
Waarom je moet stoppen met het schrijven van loops in panda’s

: toen ik Pandas voor het eerst begon te gebruiken, schreef ik altijd loops als deze:

for i in range(len(df)):
if df.loc(i, "sales") > 1000:
df.loc(i, "tier") = "high"
else:
df.loc(i, "tier") = "low"

Het werkte. En ik dacht: “Hé, het is oké, toch?”
Blijkt… niet zo veel.

Ik besefte het toen nog niet, maar loops als deze zijn een klassieke valkuil voor beginners. Ze laten Panda’s meer werk doen dan zou moeten, en ze sluipen er een mentaal model in dat je regel voor regel laat denken in plaats van kolom voor kolom.

Ooit begon ik na te denken kolomalles verandert. Codes worden korter. De uitvoering wordt sneller. En opeens voelt Panda alsof hij daarvoor gemaakt is Help mevertraagt ​​mij niet.

Om dit aan te tonen, gebruiken we een kleine dataset waarnaar we overal zullen verwijzen:

import pandas as pd
df = pd.DataFrame({
"product": ("A", "B", "C", "D", "E"),
"sales": (500, 1200, 800, 2000, 300)
})

Uitgang:

product sales
0 A 500
1 B 1200
2 C 800
3 D 2000
4 E 300

Ons doel is simpel: label elke rij als high als de omzet groter is dan 1000, omgekeerd low.

Ik zal je laten zien hoe ik het doe in eerste instantieen waarom er een betere manier is.

De lusbenadering waarmee ik begon

Dit is de lus die ik gebruik als ik leer:

for i in range(len(df)):
if df.loc(i, "sales") > 1000:
df.loc(i, "tier") = "high"
else:
df.loc(i, "tier") = "low"
print(df)

Dit levert dit resultaat op:

product sales tier
0 A 500 low
1 B 1200 high
2 C 800 low
3 D 2000 high
4 E 300 low

En ja, het werkt. Maar dit is wat ik op de harde manier heb geleerd:
Panda doet het kleine bewerkingen voor elke rijin plaats van de hele kolom in één keer efficiënt te verwerken.

Deze aanpak is niet schaalbaar: wat goed voelt met vijf rijen, vertraagt ​​met 50.000 rijen.

Wat nog belangrijker is, het zorgt ervoor dat je denkt als een beginner – regel voor regel – en niet als een professionele Panda-gebruiker.

De looptijd instellen (toen ik besefte dat deze langzaam was)

Toen ik voor het eerst de lus op deze kleine dataset uitvoerde, dacht ik: “Geen probleem, het is snel genoeg.” Maar toen vroeg ik me af… wat als ik een grotere dataset had?

Dus ik probeerde het:

import pandas as pd
import time
# Make a bigger dataset
df_big = pd.DataFrame({
"product": ("A", "B", "C", "D", "E") * 100_000,
"sales": (500, 1200, 800, 2000, 300) * 100_000
})

# Time the loop
start = time.time()
for i in range(len(df_big)):
if df_big.loc(i, "sales") > 1000:
df_big.loc(i, "tier") = "high"
else:
df_big.loc(i, "tier") = "low"
end = time.time()
print("Loop time:", end - start)

Dit is wat ik heb:

Loop time: 129.27328729629517

Dat 129 seconden.

Meer twee minuten gewoon om de rij te labelen als "high" of "low".

Toen klikte het voor mij. De code is niet alleen ‘een beetje inefficiënt’. Dat is feitelijk het op de verkeerde manier gebruiken van Panda’s.
En stel je voor dat dit in de datapijplijn gebeurt, bij vernieuwingen van het dashboard, over miljoenen rijen per dag.

Waarom zo langzaam

Deze lus dwingt Panda’s om:

  • Open elke rij één voor één
  • Voer logica op Python-niveau uit voor elke iteratie
  • Werk het DataFrame cel voor cel bij

Met andere woorden, het verandert een sterk geoptimaliseerde kolomvormige engine in een veredelde Python-lijstprocessor.

En dat is niet waarvoor Pandas is gemaakt.

Eén regel repareren (en bij klikken)

Na het zien 129 secondenIk wist dat er een betere manier moest zijn.
Dus in plaats van de regels te herhalen, probeerde ik de regel uit te drukken kolom niveau:

“Als de omzet > 1000 is, noem het dan hoog. Als dat niet het geval is, noem het dan laag.”

Alleen dat. Dat is de regel.

Hier is de vectorversie:

import numpy as np
import time

start = time.time()
df_big("tier") = np.where(df_big("sales") > 1000, "high", "low")
end = time.time()
print("Vectorized time:", end - start)

En de resultaten?

Vectorized time: 0.08

Laat dat bezinken.

Cirkelversie: 129 seconden
Vectorversie: 0,08 seconden

Het is voorbij 1.600× sneller.

Wat is er net gebeurd?

Het belangrijkste verschil is dit:

De lus verwerkt het DataFrame regel voor regel. De vectorversie verwerkt het hele ding sales kolom in één geoptimaliseerde handeling.

Wanneer je schrijft:

df_big("sales") > 1000

Pandas controleert geen individuele waarden in Python. Het voert vergelijkingen uit op een lager niveau (via NumPy), in gecompileerde code, tussen arrays.

Dan np.where() het aanbrengen van labels in één efficiënte doorgang.

Hier is een subtiele maar krachtige verandering:

In plaats van te vragen:

“Wat moet ik met deze lijn doen?”

Je vroeg:

“Welke regels zijn van toepassing op deze kolom?”

Dat is de grens tussen een beginnende Panda en een professionele Panda.

Op dit punt dacht ik dat ik ‘een hoger niveau had bereikt’. Toen ontdekte ik dat ik het eenvoudiger kon maken.

En toen ontdekte ik Booleaanse indexering

Nadat ik de vectorversie had getimed, voelde ik me behoorlijk trots. Maar toen kreeg ik een ander besef.

Ik heb het niet eens nodig np.where() hiervoor.

Laten we teruggaan naar onze kleine dataset:

df = pd.DataFrame({
"product": ("A", "B", "C", "D", "E"),
"sales": (500, 1200, 800, 2000, 300)
})

Ons doel is nog steeds hetzelfde:

Label elke regel high als de omzet > 1000, vice versa low.

Met np.where() wij schrijven:

df("tier") = np.where(df("sales") > 1000, "high", "low")

Het is schoner en sneller. Veel beter dan een lus.

Maar dit is het deel dat de manier waarop ik over Panda’s denk echt heeft veranderd:
Deze lijn hier…

df("sales") > 1000

…al iets heel nuttigs teruggegeven.

Laten we eens kijken:

Uitgang:

0 False
1 True
2 False
3 True
4 False
Name: sales, dtype: bool

Het is een Booleaanse reeks.

Pandas evalueert eenvoudigweg de toestand van de hele kolom in één keer.

Geen draai. NEE if. Er is geen lijn-voor-lijn logica.

Dit levert in één keer een volledig masker van True/False-waarden op.

Booleaanse indexering voelt als een superkracht

Hier wordt het interessant.

U kunt Booleaanse maskers rechtstreeks gebruiken om rijen te filteren:

df(df("sales") > 1000)

En Pandas geeft je meteen:

We kunnen het zelfs bouwen tier kolom gebruikt rechtstreeks Booleaanse indexering:

df("tier") = "low"
df.loc(df("sales") > 1000, "tier") = "high"

Ik zei eigenlijk:

  • Neem aan dat alles zo is "low".
  • Overschrijf alleen rijen met verkopen > 1000.

Alleen dat.

En plotseling dacht ik niet:

“Controleer voor elke rij de waarde…”

Ik denk:

“Begin met de standaardinstellingen. Pas de regels vervolgens toe op een subset.”

Deze verschuiving was subtiel, maar het veranderde alles.

Toen ik eenmaal vertrouwd raakte met Booleaanse maskers, begon ik me af te vragen:

Wat gebeurt er als de logica niet zo zuiver is als “groter dan 1000”? Wat moet ik doen als ik speciale regels nodig heb?

Daar kwam ik erachter apply(). En in eerste instantie voelde het als het beste van beide werelden.

Is dat niet zo apply() Redelijk goed?

Ik zal eerlijk zijn. Toen ik stopte met het schrijven van loops, dacht ik dat ik het allemaal doorhad. Omdat er een magische functie is die alles lijkt op te lossen:
apply().

Het voelt als de perfecte middenweg tussen rommelige loops en enge vectorisering.

Dus natuurlijk begon ik dit soort dingen te schrijven:

df("tier") = df("sales").apply(
lambda x: "high" if x > 1000 else "low"
)

En op het eerste gezicht?

Dit ziet er goed uit.

  • NEE for cirkel
  • Geen handmatige indexering
  • Gemakkelijk te lezen

Hij gevoeld als een professionele oplossing.

Maar dit is wat ik op dat moment niet begreep:

apply() voert nog steeds Python-code uit voor elke regel.
Het verbergt alleen de cirkel.

Wanneer u gebruik maakt van:

df("sales").apply(lambda x: ...)

Panda’s nog steeds:

  • Neemt elke waarde
  • Geef het door aan een Python-functie
  • Retourneert het resultaat
  • Herhaal dit voor elke rij

Het is schoner dan a for cirkel, ja. Maar qua prestaties? Dit komt dichter bij looping dan bij echte vectorisatie.

Daar werd ik een beetje wakker van. Ik realiseerde me dat ik zichtbare lussen had vervangen door onzichtbare.

Dus wanneer moet je het gebruiken? apply()?

  • Als de logica kan worden uitgedrukt met vectorbewerkingen → doe dat dan.
  • Als het kan worden uitgedrukt met een Booleaans masker → doe dat dan.
  • Als het echt aangepaste Python-logica vereist → gebruik het dan apply().
    Met andere woorden:

Eerst vectoriseren. Grijp het apply()only wanneer je zou moeten.
Niet omdat apply() slecht. Maar omdat Pandas het snelste en schoonste is als je aan kolomfuncties denkt, niet aan rijfuncties.

Conclusie

Terugkijkend was de grootste fout die ik maakte het niet schrijven van loops. Er wordt aangenomen dat als de code werkt, deze goed genoeg is.

Panda straft je niet meteen als je in lijntjes denkt. Maar naarmate uw datasets groeien, naarmate uw pijplijn schaalt, naarmate uw code zijn weg vindt naar productiedashboards en workflows, worden de verschillen duidelijk.

  • Het lijn-voor-lijn denken schaalt niet.
  • Verborgen Python-lussen schalen niet.
  • Regels op kolomniveau kunnen dat wel.

Dat is het echte verschil tussen beginners en professionals die Panda’s gebruiken.

Dus samengevat:

Stop met vragen wat je met elke regel moet doen. Begin met de vraag welke regels van toepassing zijn op de hele kolom.

Zodra u deze wijzigingen heeft aangebracht, wordt uw code sneller, overzichtelijker, gemakkelijker te beoordelen en gemakkelijker te onderhouden. En je begint meteen inefficiënte patronen te zien, ook die van jezelf.

Nieuwsbron

LAAT EEN REACTIE ACHTER

Vul alstublieft uw commentaar in!
Vul hier uw naam in