MetricSign
NL|ENStart free →
Best Practices7 min·

PySpark split() verwijdert stilletjes gegevens wanneer je aanname over het scheidingsteken onjuist is.

Het splitsen en ophalen van items werkt perfect met voorbeeldgegevens. Productiestrings bevatten echter spaties aan het einde, ingesloten scheidingstekens en ontbrekende velden, waardoor je kolommen zonder waarschuwing null worden.

Read this article in English →

getItem() retourneert null in plaats van een foutmelding te geven, en dat is het probleem.

Een veelvoorkomend patroon in Databricks-notebooks ziet er als volgt uit: neem een kolom met strings, splits deze op een scheidingsteken en gebruik getItem(i) om elk token in een eigen kolom te plaatsen. De Stack Overflow-versie van dit probleem gebruikt een door spaties gescheiden string met vier velden: type, datum, tijd en een veld voor opmerkingen dat zelf ook spaties kan bevatten.

```python from pyspark.sql import functions as F

df = df.select(F.split('content', ' ', 4).alias('row')) cols = ['type', 'date', 'time', 'comments'] col_expr = [F.col('row').getItem(i).alias(c) for i, c in enumerate(cols)] df = df.select(*col_expr) ```

Dit werkt wanneer elke rij precies vier tokens bevat. Wanneer een rij drie tokens bevat, retourneert getItem(3) null. Geen uitzondering. Geen logboekvermelding. Het DataFrame-schema blijft intact, de taak meldt succes en de kolom met opmerkingen vult zich stilletjes met null-waarden voor elke rij die niet aan de aanname voldeed.

Het gevaar schuilt niet in het codepatroon zelf, maar in het ontbreken van feedback wanneer aannames niet kloppen. Een Databricks-taak die deze logica gebruikt binnen een Bronze-to-Silver-transformatie kan wekenlang succesvol verlopen, waarbij rijen met null-waarden in Delta-tabellen worden geschreven. Vervolgaggregaties, joins op die kolommen of BI-rapporten die filteren op niet-null-waarden verliezen allemaal stilletjes rijen. Tegen de tijd dat iemand een verschil in telling opmerkt, heeft het probleem zich al tientallen keren voorgedaan.

De parameter 'limit' bepaalt waar de laatste kolom de rommel absorbeert.

Het derde argument van F.split() — de parameter limit — maakt het verschil tussen een kwetsbare splitsing en een productieklare splitsing. Wanneer je F.split('content', ' ', 4) aanroept, splitst PySpark op de eerste drie spaties en plaatst alles wat overblijft in het vierde element van de array. Zonder deze parameter splitst F.split('content', ' ') op elke spatie, waardoor een commentaarveld zoals "server timed out during sync" vijf afzonderlijke tokens wordt in plaats van één.

De parameter limit is toegevoegd in Spark 3.0. Als je Databricks Runtime 7.x of later gebruikt, is deze beschikbaar. Op oudere runtimes heb je een workaround nodig: splits zonder limit en gebruik vervolgens F.concat_ws(' ', F.slice(F.col('row'), 4, F.size(F.col('row')))) om de laatste tokens weer samen te voegen.

Er is een subtiliteit die het waard is om te weten. Wanneer limit is ingesteld op een waarde die hoger is dan het werkelijke aantal tokens, vult PySpark de array niet aan met nullen. Het retourneert een kortere array. Dus F.split('content', ' ', 4) op de string "ERROR 2026-05-01" produceert een array van lengte 2, niet van lengte 4 met twee nullen aan het einde. Dit betekent dat getItem(2) en getItem(3) beide null retourneren, maar F.size(F.col('row')) retourneert 2. Die groottecontrole is je eerste verdedigingslinie.

Een defensieve versie van de extractie voegt een filter of een markeringskolom toe:

``python df = df.withColumn('row', F.split('content', ' ', 4)) df = df.withColumn('token_count', F.size('row')) df_valid = df.filter(F.col('token_count') == 4) df_malformed = df.filter(F.col('token_count') != 4) ``

Stuur df_malformed door naar een quarantainetabel. Verwerk df_valid normaal. Dit voegt misschien twee seconden toe aan een taak die miljoenen rijen verwerkt, maar het voorkomt de langzame achteruitgang van de datakwaliteit die later veel duurder is om te diagnosticeren.

Defensive string splitting pipeline Trim leading/trailing whitespace with Split with regex delimiter and limit parameter Check token count with F.size() Route rows with wrong count to quarantine table Extract columns with getItem() from valid rows Assert quarantine rate below threshold
Defensive string splitting pipeline

Reguliere expressies splitsen zich anders dan je zou verwachten bij opeenvolgende overeenkomsten.

Door spaties gescheiden gegevens zijn zelden netjes gescheiden. Logbestanden, geëxporteerde platte bestanden en gekopieerde tekst bevatten vaak opeenvolgende spaties, tabs gemengd met spaties, of overtollige witruimte aan het einde. De standaardfunctie F.split('content', ' ') behandelt elke afzonderlijke spatie als een scheidingsteken, dus "ERROR 2026-05-01" (twee spaties) produceert ['ERROR', '', '2026-05-01'] — een lege tekenreeks komt op index 1 te staan en je datum belandt in de tijdkolom.

De oplossing is een regex-scheidingsteken: F.split('content', '\\s+', 4). Dit voegt opeenvolgende witruimte samen tot één splitsingspunt. Let op de dubbele escape-tekens — PySpark geeft het patroon door aan Java's String.split(), die regex interpreteert, en de Python-tekenreeks heeft een eigen escape-laag nodig. In een Databricks-notebookcel is '\\s+' correct. In een .py-bestand dat als module wordt geladen, is r'\s+' met een onbewerkte tekenreeks echter netter.

Een andere veelvoorkomende valkuil: spaties aan het begin van een tekenreeks. Als je tekenreeks met een spatie begint, produceert split('\\s+') een lege tekenreeks als eerste element. De array wordt dan ['', 'ERROR', '2026-05-01', '12:00:00', 'timeout'] — vijf elementen, met een lege tekenreeks op index 0. je kolom 'type' bevat nu een lege tekenreeks en 'comments' wordt afgekapt omdat de limiet de splitsing al heeft beperkt.

De veiligste aanpak combineert F.trim() vóór het splitsen:

``python df = df.withColumn('content_clean', F.trim(F.col('content'))) df = df.withColumn('row', F.split('content_clean', '\\s+', 4)) ``

Dit verwijdert spaties aan het begin en einde van de kolom voordat de splitsing wordt uitgevoerd. Het is een kleine toevoeging die een aantal fouten voorkomt die notoir moeilijk te vinden zijn in uitvoergegevens, omdat lege strings en verschoven kolommen er niet overduidelijk verkeerd uitzien in een .show(5)-voorbeeldweergave — vooral wanneer de eerste vijf rijen toevallig schoon zijn.

Wanneer de splitsing binnen een geplande taak wordt uitgevoerd, worden fouten onzichtbaar.

De werkelijke kosten van een foute splitsing zitten niet in de verkeerde uitvoer bij één run. Het zit hem in de verkeerde uitvoer bij elke run, ongemerkt, binnen een geplande Databricks-workflow.

Neem bijvoorbeeld een dagelijkse taak die log bestanden verwerkt, elke regel in kolommen splitst en naar een Silver Delta-tabel schrijft. Op dag één produceert het bronsysteem netjes geformatteerde logs. Op dag dertig voegt een configuratiewijziging in het bronsysteem een extra veld toe of verandert het scheidingsteken van een spatie naar een tab. De splitsingslogica faalt niet. Het produceert arrays met een onverwachte lengte, getItem() retourneert null-waarden voor de nieuwe posities en de taak schrijft rijen met null-waarden in kolommen die voorheen wel gevuld waren.

Databricks-taken worden uitgevoerd met een groen vinkje. De clusterlogs tonen geen waarschuwingen. Het schrijven naar de Delta-tabel lukt omdat het schema niet is gewijzigd — de kolommen bestaan nog steeds, ze bevatten alleen null-waarden. Als je het aantal rijen controleert, komt dat ook overeen, omdat hetzelfde aantal invoerrijen hetzelfde aantal uitvoerrijen heeft opgeleverd.

Drie signalen kunnen dit probleem signaleren voordat het verergert. Ten eerste, voeg een controle na het schrijven toe aan je notebook die de null-percentages per kolom controleert: df.select([F.mean(F.col(c).isNull().cast('int')).alias(c) for c in cols]). Als het null-percentage voor een kolom de historische basislijn overschrijdt, moet de taak mislukken. Ten tweede, houd de verdeling van token_count bij over de verschillende uitvoeringen. Een plotselinge piek in rijen met minder tokens dan verwacht duidt op een schemawijziging. Ten derde, gebruik schema-evolutie met de nodige voorzichtigheid — mergeSchema in Delta kan structurele problemen maskeren door stilletjes kolommen toe te voegen in plaats van de inconsistentie aan het licht te brengen.

MetricSign monitort de resultaten van Databricks-taken en brengt afwijkingen in de uitvoeringsstatistieken aan het licht over opeenvolgende uitvoeringen. Een taak die verhoogde null-percentages of verschoven kolomverdelingen begint te produceren, activeert een waarschuwing met context over de onderliggende oorzaak, in plaats van het probleem over meerdere uitvoeringen te laten voortduren.

Een compleet verdedigingspatroon voor het splitsen van productiestrings.

Samengevat, hier is een patroon dat de meest voorkomende fouten – ontbrekende tokens, opeenvolgende scheidingstekens, spaties aan het begin en schema-afwijkingen – in één transformatieblok afhandelt:

```python from pyspark.sql import functions as F

EXPECTED_COLS = ['type', 'date', 'time', 'comments'] EXPECTED_COUNT = len(EXPECTED_COLS)

df = (

df

.withColumn('content_clean', F.trim(F.col('content')))

.withColumn('tokens', F.split('content_clean', '\\s+', EXPECTED_COUNT))

.withColumn('token_count', F.size('tokens')) )

df_quarantine = df.filter(F.col('token_count') != VERWACHT_AANTAL) df_quarantine.write.mode('append').saveAsTable('bronze.quarantine_logs')

df_valid = df.filter(F.col('token_count') == VERWACHT_AANTAL) col_expr = [F.col('tokens').getItem(i).alias(c) for i, c in enumerate(VERWACHT_KOLOMMEN)] df_result = df_valid.select('content', *col_expr) ```

De quarantainetabel dient twee doelen. Het biedt een plek om onjuist opgemaakte rijen te inspecteren zonder de pipeline te onderbreken. En het aantal rijen in de loop van de tijd fungeert als een kanarie — een plotselinge toename betekent dat er iets stroomopwaarts is veranderd.

Voor taken waarbij geen enkel gegevensverlies is toegestaan, voegt je een tellingassertie toe:

```python totaal = df.count() geldig = df_valid.count() in quarantaine geplaatst = df_quarantine.count() assert totaal == geldig + in quarantaine geplaatst

QUARANTINE_THRESHOLD = 0.05 als in quarantaine geplaatst / totaal > QUARANTINE_THRESHOLD:

raise ValueError(f"Quarantainepercentage {in quarantaine geplaatst/totaal: 0,2%} overschrijdt de drempelwaarde") ```

De drempelwaarde is per bron instelbaar. Een quarantainepercentage van 5% is wellicht normaal voor een rommelige logverwerking, maar onacceptabel voor een gestructureerde export. Door dit expliciet in te stellen, wordt het team gedwongen een beslissing te nemen over acceptabele datakwaliteit in plaats van het antwoord achteraf te ontdekken wanneer een dashboard er onjuist uitziet. Dit patroon voegt ongeveer tien regels toe aan een transformatie die de meeste teams in drie regels schrijven. Die tien regels maken het verschil tussen een pipeline die werkt en een die werkt totdat hij er stilletjes mee ophoudt.

Veelgestelde vragen

Waarom produceert PySpark split() met een limiet nog steeds arrays die korter zijn dan de limietwaarde?+
De parameter `limit` begrenst het maximale aantal splitsingen, niet de minimale lengte van de array. Als de invoerstring minder tokens bevat dan de limiet, heeft de resulterende array simpelweg minder elementen. Er vindt geen opvulling plaats. Gebruik `F.size()` op de resulterende array om de lengte te controleren voordat je `getItem()` aanroept.
Hoe kan ik een tekenreeks splitsen op een spatie als scheidingsteken, zonder dat velden die spaties bevatten, worden afgebroken?+
Gebruik de parameter `limit` om het aantal splitsingen te beperken. `F.split('col', ' ', 4)` splitst op de eerste drie spaties en plaatst de rest – inclusief eventuele spaties – in het vierde element. Hierdoor blijven meerwoordige waarden in de laatste kolom behouden. Voor velden midden in een tekenreeks die spaties bevatten, hebt je een andere scheidingsteken of een reguliere expressie nodig die overeenkomt met de werkelijke veldgrenzen.

Gerelateerde integraties

Gerelateerde artikelen