Het filteren van data met behulp van ID == ‘string’ in Pandas moet u vermijden, omdat de scalar_compare operator tot prestatieproblemen leidt. Er zijn veel manieren om dit te omzeilen, bijvoorbeeld door uw Dataframe te partitioneren in een woordenboek met de ID in kwestie als sleutel.
Inleiding | String filters in pandas
De vereenvoudiging van het hardwarebeheer die cloud oplossingen met zich meebrengen, dwingt ons meer en meer om ons af te keren van de problemen van codeoptimalisatie.

Maar opschalen en meer rekenkracht is niet altijd de oplossing omdat dit tot een escalatie van kosten leidt en rekenkracht niet oneindig is.
Door mezelf de uitdaging te geven om al mijn data preparaten op een eenvoudige container te plaatsen, realiseerde ik me al snel dat met de kennis van een paar trucjes, Het optimaliseren van uw code kan soms net zo eenvoudig zijn als het instanceren van een grotere instantie.
In dit artikel wil ik terugkomen op één codewijziging waarmee ik de tijd die besteed werd aan het berekenen van kenmerken tijdens de ontwikkelingsfase van een propensity model, drastisch kon verminderen. Deze wijziging is algemeen genoeg om op veel andere situaties te worden toegepast.

Het is niet de bedoeling om de meest optimale oplossing te zijn, maar om een snelle optie te zijn om de rekentijd op een efficiënte manier te verminderen, in de geest van de Pareto-principe.
Context
Tijdens deze missie was ik verantwoordelijk voor het automatiseren van het data voorbereidingsproces en de voorspelling van modellen die ontwikkeld waren door ons Data Scientist team. Om de data-flow te vereenvoudigen, werd transactionele data elke dag geüpload en moest het door een eerste Voorbewerking stap, gevolgd door een tweede stap van Functie Berekening voordat u de laatste stap van Model voorspelling met behulp van het getrainde model.

De Berekening van kenmerken fase is degene die het langst duurde om uit te voeren: er werden inderdaad veel kenmerken berekend op klantniveau, wat resulteerde in het herhaaldelijk uitvoeren van een verrassend tijdrovende regel in de code :

Deze enkele regel had een tijd van 18 ms, wat betekende dat ik met mijn 33.717 klanten die ik dagelijks moest evalueren, ongeveer 10 minuten aan ruwe rekentijd besteedde per functie op klantniveau, teruggebracht tot 1 min en 16 seconden per functie dankzij de parallellisatie van de bewerking op mijn 8 beschikbare CPU's.
Omdat we met B2B data werkten, was het nodig om kenmerken op klantniveau te berekenen, aangezien één klant in feite één bedrijf vertegenwoordigde met soms meerdere bestellingen per dag.
Experimenteren met String-filters in pandas
Na wat onderzoek met behulp van de %%prun magische commando, kon ik de bron van dit verwerkingsprobleem identificeren: de pandas._libs.ops.scalar_compare operator die ondergeoptimaliseerd was in mijn versie van Pandas (1.3.1).

Door deze “==” operator door “isin”, wat niet erg intuïtief is omdat ik een enkele string vergeleek, heb ik de rekentijd al gedeeld door 2,5 keer, waardoor ik van 18ms per bewerking naar 7,95ms ging.

Nog steeds op zoek naar optimalisatie, kwam ik een Stackoverflow bericht waarin het gebruik van Categorisch type om de werking verder te verbeteren.

Met deze laatste implementatie kon ik de rekentijd meer dan 36 keer. Ik kon echter een nuance in deze truc ontdekken, zoals het categorietype gedraagt zich niet als een klassieke str tijdens alle bewerkingen (zie onderstaand voorbeeld bij gebruik van .groupby() in pandas), dus ik moest het op een gegeven moment terug converteren naar str.

Hypothese
Maar waarom is dat? Hoe kan een eenvoudige == bewerking tussen twee strings kost meer tijd dan een isin() bewerking die lijsten vergelijkt, of een categoriaal een ?
Welnu, om de eerste vraag te beantwoorden moeten we zeker de code achter de uitvoering van scalair_vergelijken dat voornamelijk Cython gebruikt en vergelijk het met de code achter de isin() methode.
Gelukkig lijkt het antwoord op het tweede deel intuïtiever: wanneer we twee tekenreekswaarden vergelijken, vergelijken we een oneindig aantal van mogelijkheden samen, terwijl bij het vergelijken van twee categorieën, de aantal opties is ingesteld door de verschillende unieke categorieën die er bestaan. Het lijkt veel gemakkelijker om twee entiteiten te vergelijken als ons aantal opties vast is.
Mijn dorst naar optimalisatie was nog steeds niet gelest, dus besloot ik een stapje terug te doen ten opzichte van de huidige methode. Ik bedacht een nieuwe aanpak: mijn Dataframe partitioneren in een woordenboek die ik dan gebruik om mijn klanten te filteren bij het berekenen van mijn kenmerken.
In termen van code vertaalde dit zich eenvoudigweg in de volgende regels:
Het opbouwen van dat woordenboek kostte me 32 seconden rekentijd, maar met dit gepartitioneerde Dataframe kon ik mijn data nu in een paar nanoseconden filteren.

Conclusie | String filters in pandas
Na een paar uur experimenteren was ik tevreden met het resultaat:

De aanvankelijke rekentijd per klantfiltering werd nu verdeeld 348 000 keer, van 18 ms tot 51,7ns, of van 10min tot 2,65ms per functie berekend, rekening houdend met de tijd die besteed is aan het partitioneren.

De impact van deze kleine verandering stelde me onmiddellijk in staat om de rekentijd van mijn volledige Feature berekeningsfase verminderen met 90%, van 40’49” naar 7’27”. Met behulp van een CO2eq-schattingsmethode die ik in mijn volgende artikel in detail zal beschrijven, is deze wijziging bespaarde minstens 170$/jaar + 22kgCO2/jaar en mogelijk nog veel meer met de groeiende klantenlijst en de uitrol van het project in andere landen.

BLOG







