Adatok tisztítása és előkészítése a Python segítségével az adattudomány számára - legjobb gyakorlatok és hasznos csomagok

Előszó

Az adatok tisztítása csak valami, amivel foglalkozni kell az elemzés során. Ez nem nagy munka, de el kell végezni, hogy nagyszerű munkát végezzen.

Olyan sok időt töltöttem a funkciók írásával és átírásával, hogy segítsen nekem az adatok tisztításában, hogy meg is szerettem volna osztani néhányat, amit megtanultam az út során. Ha még nem tért át ezen a poszton, akkor nézd meg, hogyan lehetne jobban megszervezni az adattudományi projekteket, mivel ez segít megfogalmazni néhány, az alábbiakban átvitt fogalmat.

Miután jobban rendeztem a kódomat, elkezdtem egy egyedi csomagot tartani, ahol megőriztem a „kitisztítási” kódomat. Ha még valami, ez alapot ad nekem az egyedi módszerek írásához olyan adatokra, amelyek nem igazán felelnek meg az előző tisztítási szkripteimnek. És nem kell 100. alkalommal írnom azt a regex e-mail kivonatot, mert hozzáférhető helyen mentettem el.

Egyes vállalatok egész csapatának elkötelezett a kód tisztítása mellett, de a legtöbb nem. Tehát a legjobb, ha megértjük a bevált gyakorlatok néhányát. Ha van valami, akkor jobban megérti adatainak szerkezetét, hogy jobban megmagyarázza, miért vagy miért nem történt valami.

Ezenkívül a posztra való felkészülés során átfutottam a kjam ezen repóját, amely hihetetlenül hasznos lett volna, amikor először megtanultam az adatok tisztítását. Ha el szeretne mélyülni a kódtisztításban, azt javaslom, kezdje meg ott.

A cél az, hogy takarítsd meg a dolgokat… vagy legalább megpróbáld megtenni

Ellenőrizze adatait ... Gyorsan

Az első dolog, amelyet új adatkészlet megszerzésekor meg kell tennie, a tartalom gyors ellenőrzése a .head () módszerrel.

importálj pandákat pd-ként
df = pd.read_csv ('elérési út_adata')
df.head (10)
>>
... néhány kimenet itt ...

Most nézzük meg gyorsan az oszlopok nevét és típusát. Leggyakrabban olyan adatokat fog kapni, amelyek nem egészen a vártakhoz tartoznak, például a dátumokat, amelyek valójában karakterláncok és egyéb furcsa tényezők. De előzetesen ellenőrizni.

# Get oszlopnevek
oszlop_nevek = df.oszlop
print (COLUMN_NAMES)
# Oszlop adattípusok beszerzése
df.dtypes
# Ellenőrizze azt is, hogy az oszlop egyedi-e
az i oszlopnevekben:
  print ('{} egyedi: {}'. formátum (i, df [i] .is_unique))

Most nézzük meg, hogy az adatkerethez van-e társítva index, a df-en található .index hívásával. Ha nincs index, akkor AttributeError jelenik meg: A 'function' objektumon nem jelenik meg az 'index' attribútum hiba.

# Ellenőrizze az index értékeit
df.index.values
# Ellenőrizze, létezik-e egy bizonyos index
„foo” a df.index.values-ben
# Ha nem létezik index
df.set_index ('oszlop_név_felhasználás', inplace = igaz)

Jó. Adatainkat gyorsan ellenőriztük, tudjuk az adattípusokat, ha az oszlopok egyediek, és tudjuk, hogy van indexe, így később csatlakozhatunk és egyesíthetünk. Gondoljuk ki, mely oszlopokat szeretné megtartani vagy eltávolítani. Ebben a példában meg akarunk szabadulni az 1., 3. és 5. index oszlopaitól, tehát csak hozzáadtam a string értékeket egy listához, amelyet az oszlopok ledobására használnak.

# Készítse el az elveszíteni kívánt oszlopok listájának megértését
columns_to_drop = [oszlopnevek [i] i-re az [1, 3, 5] -ben]
# Dobjon el nem kívánt oszlopokat
df.drop (oszlopok_to_drop, helyettes = igaz, tengely = 1)

Az inplace = True hozzá lett adva, így nem kell megtakarítania az eredeti df-et, ha a .drop () eredményt hozzárendeli a df-hez. A pandák sok módszerének támogatása az inplace = True, ezért próbálja meg a lehető legnagyobb mértékben használni, hogy elkerülje a felesleges újrakiosztást.

Mi a teendő a NaN-nal?

Ha hibákat vagy hiányokat kell kitöltenie, használja a fillna () és dropna () metódusokat. Gyorsnak tűnik, de minden adatkezelést dokumentálni kell, hogy később megmagyarázhassa azokat valakinek.

Meg lehet tölteni a NaN-kat karakterlánccal, vagy ha számok, használhatja az átlagot vagy a medián értéket. Sok vita folyik arról, hogy mi történik a hiányzó vagy hibás adatokkal, és a helyes válasz az, hogy… attól függ.

A legjobb megítélést és az általuk dolgozott emberek bemeneteit kell felhasználnia annak eldöntésére, hogy miért a legjobb módszer az adatok eltávolítása vagy kitöltése.

# Töltse ki a NaN-t ''
df ['col'] = df ['col']. fillna ('')
# Töltsük meg a NaN értéket 99-rel
df ['col'] = df ['col']. fillna (99)
# Töltsük meg a NaN értéket az oszlop átlagával
df ['col'] = df ['col']. fillna (df ['col']. mean ())

A nem null értékeket előre vagy hátra is terjesztheti, ha a módszer = "pad" metódus argumentum. Ez kitölti az adatkeret következő értékét az előző nem NaN értékkel. Lehet, hogy csak egy értéket akar kitölteni (limit = 1), vagy ki szeretné tölteni az összes értéket. Bármi legyen is, ellenőrizze, hogy összhangban van-e az adattisztítás többi részével.

df = pd.DataFrame (adatok = {'col1': [np.nan, np.nan, 2,3,4, np.nan, np.nan]})
    col1
0 NaN
1 NaN
2 2.0
3 3.0
4 4.0 # Ez az érték, amelyet kitölteni kell
5 NaN
6 NaN
df.fillna (módszer = 'pad', limit = 1)
    col1
0 NaN
1 NaN
2 2.0
3 3.0
4 4.0
5 4.0 # Előre töltve
6 NaN

Figyelem, hogyan töltötte be csak az 5. mutatót? Ha nem töltöttem volna be korlátozottan a táblát, akkor az megtörtént volna a teljes adatkeret. Nem korlátozódunk az előzetes kitöltésre, hanem a bfill kitöltésével is.

# Töltse ki az első két NaN értéket az első elérhető értékkel
df.fillna (módszer = 'bfill')
    col1
0 2.0 # Töltve
1 2.0 # kitöltve
2 2.0
3 3.0
4 4.0
5 NaN
6 NaN

Eldobhatja őket teljesen az adatkeretből, akár sorban, akár oszlopban.

# Dobjon el minden olyan sort, amelyben nans van
df.dropna ()
# Dobjon el olyan oszlopokat, amelyekben nans van
df.dropna (tengely = 1)
# Csak azokat az oszlopokat dobja le, amelyek legalább 90% -a nem NaN-t tartalmaz
df.dropna (thresh = int (df.shape [0] * .9), tengely = 1)

A thresh = N paraméter megköveteli, hogy az oszlop legalább N nem NaN-t tartalmazzon a túléléshez. Gondoljon erre az hiányzó adatok alsó korlátjára, amelyet elfogadhatónak talál az oszlopokban. Fontolja meg azokat a naplózási adatokat, amelyeknél hiányozhat bizonyos szolgáltatások gyűjteménye. Csak azt a nyilvántartást szeretné, amelyben a rendelkezésre álló szolgáltatások 90% -a megtörténik, mielőtt azt a modell jelöltjeként kezeli.

np.where (if_this_is_true, do_this, else_do_that)

Bűnös vagyok azért, hogy ezt korábban nem használtam elemző karrierem során, mert ez túlmutathatatlan. Olyan sok időt és csalódást takarít meg, amikor egy adatkereten keresztül keres. Ha gyorsan meg szeretne végezni néhány alapvető takarítást vagy a szolgáltatás megtervezését, itt olvashatja el a következő címet: np.

Fontolja meg, hogy értékel-e egy oszlopot, és azt szeretné tudni, hogy az értékek szigorúan meghaladják-e a 10-et. Ha igen, akkor az eredmény 'foo', ha nem, akkor az eredmény 'bar'.

# Kövesse ezt a szintaxist
np.where (if_this_condition_is_true, do_this, else_this)
# Példa
df ['new_column'] = np.where (df [i]> 10, 'foo', 'bar)

Összetettebb műveleteket hajthat végre, mint például az alábbi. Itt megvizsgáljuk, hogy az oszloprekord foo-val kezdődik-e, és nem ér véget sávval. Ha ez megtörténik, akkor visszatérítjük az Igaz értéket. Egyébként az oszlopban visszatérítjük az aktuális értéket.

df ['new_column'] = np.where (df ['col']. str.startswith ('foo') és
                            nem df ['col']. str.endswith ('bar'),
                            Igaz,
                            df [ 'col'])

És még hatékonyabb, ha elkezdesz fészkelni az np.hova, hogy egymásra rakódjanak. Hasonlóan ahhoz, ahogyan összerakná a háromoldalú műveleteket, győződjön meg arról, hogy azok olvashatók-e, mivel az erősen beágyazott utasításokkal gyorsan rendetlenségbe kerülhet.

# Három szintű fészkelés az np.where-vel
np.where (if_this_condition_is_true_one, do_this,
  np.where (if_this_condition_is_true_two, do_that,
    np.where (ha_this_condition_is_true_three, do_foo, do_bar)))
# Egy triviális példa
df ['foo'] = np.where (df ['bar'] == 0, 'Zero',
              np.where (df ['bar'] == 1, 'One',
                np.where (df ['bar'] == 2, 'kettő', 'három')))

Állítsa be és tesztelje, mi van

Hitel a https://www.programiz.com webhelyen

Csak azért, mert adatai szép adatkeretben vannak, nincs másolat, nincs hiányzó érték, továbbra is problémái lehetnek az alapul szolgáló adatokkal. És 10M + soros adatkerettel vagy új API-val, hogyan lehet megbizonyosodni arról, hogy az értékek pontosan megfelelnek-e azoknak, amelyeket elvárnak?

Az igazság az, hogy soha nem fogja tudni, hogy adatai helyesek-e, amíg nem teszteli. A szoftverfejlesztés bevált gyakorlatai nagyban támaszkodnak munkájuk tesztelésére, de az adattudomány szempontjából ez még mindig folyamatban van. Inkább kezdje el most, és tanítsa meg magának a jó munka alapelveit, ahelyett, hogy később újraképznie kellene.

Készítsünk egy egyszerű adatkeretet a teszteléshez.

df = pd.DataFrame (adatok = {'col1': np.random.randint (0, 10, 10), 'col2': np.random.randint (-10, 10, 10)})
>>
   col1 col2
0 0 6
1 6 -1
2 8 4
3 0 5
4 3 -7
5 4 -5
6 3 -10
7 9 -8
8 0 4
9 7 -4

Vizsgáljuk meg, hogy a col1 összes értéke> = 0 legyen - a beépített módszertan használatával, amely a Python szabványos könyvtárához tartozik. Mit kérdezel a python-tól, ha igaz, a df ['col1'] -ben szereplő összes elem nagyobb, mint nulla. Ha ez igaz, akkor folytassa az utat, ha nem dob be hibát.

assert (df ['col1']> = 0) .all () # Semmit nem szabad visszaadnia

Úgy tűnik, hogy nagyszerűen működött. De mi van, ha az .all () nem került bele az állításba?

assert (df ['col1']> = 0)
>>
ValueError: A sorozat igazságértéke nem egyértelmű. Használjon a.empty, a.bool (), a.item (), ayan () vagy a.all ().

A Humm úgy tűnik, hogy van néhány lehetőségünk az adatkeretek tesztelésekor. Tegyük fel, hogy az értékek bármelyike ​​karakterlánc.

assert (df ['col1']! = str) .any () # Semmit nem szabad visszaadnia

Mi lenne a két oszlop tesztelésével, hogy kiderüljön-e egyenlők?

assert (df ['col1'] == df ['col2']). összes ()
>>
Traceback (legutóbbi hívás utolsó):
  "" fájl, 1. sor, a  -ban
AssertionError

Ah, állításunk itt kudarcot vallott!

Az állításokkal kapcsolatos legjobb gyakorlat az adatain belüli feltételek tesztelése, amelyeknek soha nem szabad megtörténni. Így van, amikor a kódot futtatja, minden leáll, ha ezen állítások egyike meghiúsul.

Az .all () módszer ellenőrzi, hogy az objektumok összes eleme meghaladja-e az állítást, míg az .all () módszer ellenőrzi, hogy az objektumok bármelyik eleme megfelel-e az érvényesítési tesztnek.

Ez akkor lehet hasznos, ha:

  • Ellenőrizze, hogy vannak-e negatív értékek az adatokban;
  • Győződjön meg arról, hogy két oszlop pontosan megegyezik;
  • Meghatározza az átalakítás eredményeit, vagy;
  • Ellenőrizze, hogy az egyedi azonosítószám pontos-e.

Vannak olyan magasabb szintű módszerek, amelyeket nem áttérek, de megismerkedöm, amelyeket itt használhat. Soha nem fogja tudni, hogy mikor kell egy bizonyos állapotot tesztelnie, ugyanakkor el kell kezdenie azoknak a feltételeknek a tesztelését, amelyeket nem kíván a kódjában.

Ne próbáljon meg mindent, hanem teszteljen olyan dolgokat, amelyek megtörik a modelleket.

Például. Olyan tulajdonság, amelynek mind 0-nak és 1-nek kell lennie, és ténylegesen fel vannak töltve ezekkel az értékekkel.

Ezenkívül az a csodacsomag pandák egy tesztcsomagot is tartalmaznak.

import pandas.util.testing as tm
tm.assert_series_equal (df ['col1'], df ['col2'])
>>
AssertionError: A sorozat különbözik
A sorozat értékei különböznek (100,0%)
[balra]: [0, 6, 8, 0, 3, 4, 3, 9, 0, 7]
[jobbra]: [6, -1, 4, 5, -7, -5, -10, -8, 4, -4]

Nemcsak hibát kaptunk, hanem a pandák elmondták, mi a baj.

Szép.

Ezenkívül, ha el szeretné kezdeni magának a tesztelő csomag készítését - és érdemes gondolkodni erről - ismerkedjen meg a Python könyvtárba épített legegyszerűbb csomaggal. Erről többet megtudhat itt.

beautifier

Ahelyett, hogy saját regexét kellene írni - ami a legjobb időkben fájdalom, néha ezt az Ön számára is megtették. A kozmetikai csomag segít megtisztítani az e-mailek vagy URL-ek néhány általánosan használt mintáját. Ez semmi fantasztikus, de gyorsan segíthet a takarításban.

$ pip3 telepítő szépítő
a szépségápolási behozatalról Email, Url
email_string = 'foo@bar.com'
email = Email (email_string)
print (email.domain)
print (email.username)
print (email.is_free_email)
>>
bar.com
ize
Hamis
url_string = 'https://github.com/labtocat/beautifier/blob/master/beautifier/__init__.py'
url = URL (url_string)
print (url.param)
print (url.username)
print (url.domain)
>>
Egyik sem
{'msg': 'A szolgáltatás jelenleg csak a linkedin URL-ekkel érhető el.}
github.com

Ezt a csomagot akkor használom, ha rengeteg URL van, amelyeket át kell dolgoznom, és nem akarom a regexet 100. alkalommal írni a cím egyes részeinek kibontása céljából.

Az Unicode kezelése

Néhány NLP elvégzésekor a Unicode-val való kapcsolat a legjobb időkben frusztráló lehet. Fogok futtatni valamit a spaCy-ben, és hirtelen minden rajta fog törni, mert valami unicode karakter megjelenik valahol a dokumentumban.

Valójában ez a legrosszabb.

Az ftfy (fix az Ön számára) használatával javíthatja az igazán megtört Unicode-t. Fontolja meg, ha valaki az Unicode-ot egy szabványdal kódolta, és egy másikval dekódolta. Most ezzel a vonallal kell foglalkoznod, mint értelmetlen szekvenciákkal, amelyeket „mojibake” -nek hívnak.

# Példa a mojibake-re
& Macr; \\ _ (A \ X83 \ X84) _ / & macr;
\ ufeffParty
\ 001 \ 033 [36; 44mI & # X92; m

Szerencsére az ftfy heurisztikát alkalmaz a mojibake felismerésére és visszavonására, nagyon alacsony téves pozitív eredményekkel. Lássuk, mire alakíthatjuk át a fenti vonóságainkat, így elolvashatjuk őket. A fő módszer a fix_text (), és ezt fogod használni a dekódoláshoz.

import ftfy
foo = '& macr; \\ _ (ã \ x83 \ x84) _ / & macr;'
bar = '\ ufeffParty'
baz = '\ 001 \ 033 [36; 44mI & # x92; m'
print (ftfy.fix_text (foo))
print (ftfy.fix_text (bar))
print (ftfy.fix_text (BAZ))

Ha meg szeretné tudni, hogyan történik a dekódolás, próbálkozzon az ftfy.explain_unicode () -val. Nem hiszem, hogy ez túlzottan hasznos lesz, de érdekes látni a folyamatot.

ftfy.explain_unicode (foo)
U + 0026 & [Po] AMPERSAND
U + 006D m [Ll] LATIN SMALL L
U + 0061 a [Ll] LATIN KIS LEÍR A
U + 0063 c [Ll] LATIN KIS LEVE C
U + 0072 r [Ll] LATIN SMALL R
U + 003B; [Po] SZEMIKOLON
U + 005C \ [Po] REVERSE SOLIDUS
U + 005F _ [Pc] LOW LINE
U + 0028 ([Ps] Bal bal oldali elme
U + 00E3 ã [Ll] LATIN KIS LEÍR A TILDAL
U + 0083 \ x83 [Másolat] 
U + 0084 \ x84 [másolat] 
U + 0029) [Pe] JOGOS SZÜLETÉS
U + 005F _ [Pc] LOW LINE
U + 002F / [Po] SOLIDUS
U + 0026 & [Po] AMPERSAND
U + 006D m [Ll] LATIN SMALL L
U + 0061 a [Ll] LATIN KIS LEÍR A
U + 0063 c [Ll] LATIN KIS LEVE C
U + 0072 r [Ll] LATIN SMALL R
U + 003B; [Po] SZEMIKOLON
Egyik sem

Dedupe

Ez egy olyan könyvtár, amely gépi tanulást használ a duplikáció és az entitás felbontásának gyors végrehajtására a strukturált adatokon. Van egy nagyszerű üzenet, amely sokkal részletesebben megy be, mint én, és amelyre erősen rajzoltam.

Letöltjük a chicagoi korai gyermekkori helyadatokat, amelyek itt megtalálhatók. Van egy csomó hiányzó érték és duplikált érték a különböző adatforrásokból, ezért érdemes tovább tanulni.

Ha korábban már átmészelt másolatot, ez nagyon ismerősnek tűnik.

# Oszlopok és az egyes hiányzó értékek száma
Az Id 0 na értéke van
A forrás 0 na értéket tartalmaz
A webhelynév értéke 0 nincs
A cím 0 na értéke van
A Zip 1333 na értéke van
A telefon 146 na értékkel rendelkezik
A fax értéke 3299 na
A programnév 2009 na értékeket tartalmaz
A nap hosszának na értéke 2009 van
Az IDHS szolgáltató azonosítójának 3298 na értéke van
Az ügynökségnek 3325 na értéke van
A környéken 2754 na érték van
A finanszírozott beiratkozásnak 2424 na értéke van
A program opció 2800 na értékkel rendelkezik
Az EHS-enkénti szám 3319 na értékkel rendelkezik
A helyenkénti szám száma 3319 na
A rendező 3337 na értékkel rendelkezik
A Head Start Fund 3337 na értékkel rendelkezik
Az Eearly Head Start Fund 2881 na értékkel rendelkezik
A CC alap 2818 na értékkel rendelkezik
A Progmod 2818 na értékkel rendelkezik
A weboldal 2815 na értékkel rendelkezik
Az ügyvezető igazgató 3114 na értékkel rendelkezik
A központ igazgatója 2874 na értékkel rendelkezik
Az ECE elérhető programok 2379 na értékkel rendelkeznek
A NAEYC érvényben van 2968 na értékkel
A NAEYC Program Id 3337 na értékkel rendelkezik
Az e-mail cím 3203 na értéket tartalmaz
Uncia megelőzés: A leírásnak 3185 na értéke van
A lila kötőanyag szolgáltatási típus 3215 na értékkel rendelkezik
Az oszlop értéke 3337 na
A 2. oszlop 3018 na értékkel rendelkezik

A dedupe által biztosított előfeldolgozási módszerre van szükség annak biztosításához, hogy a modell mintavételi és képzési szakaszaiban ne forduljanak elő hibák. Bízz bennem, ennek használata megkönnyíti a dedupe használatát. Mentse el ezt a módszert a helyi „tisztítócsomagba”, hogy később is használhassa, ha másolatot készít.

importálj pandákat pd-ként
import csupa
import dedupe
import os
import csv
import re
from unidecode import Unidecode
def preProcess (oszlop):
    „””
    A dedupció során fellépő hibák megelőzésére szolgál.
    „””
    próbálja meg :
        oszlop = column.decode ('utf8')
    kivéve AttributeError:
        elhalad
    oszlop = unidecode (oszlop)
    oszlop = re.sub ('+', '', oszlop)
    oszlop = re.sub ('\ n', '', oszlop)
    oszlop = oszlop.strip (). szalag ('' '). szalag ("'"). alsó (). szalag ()
    
    ha nem oszlop:
        oszlop = Nincs
    visszatérő oszlop

Most kezdje el importálni a .csv oszlopot oszlopról, az adatok feldolgozása közben.

def readData (fájlnév):
    
    data_d = {}
    megnyitva (fájlnév) mint f:
        olvasó = csv.DictReader (f)
        az olvasó sorában:
            clean_row = [(k, preProcess (v)) a (k, v) számára a sorban.items ()]
            sor_id = int (sor ['Id'])
            data_d [row_id] = dict (tiszta_row)
visszatér df
name_of_file = 'data.csv'
print ('Adatok tisztítása és importálása ...')
df = readData (fájl_neve_neve)

Most meg kell mondanunk dedupet, hogy milyen tulajdonságokat kell figyelembe vennünk az ismétlődő értékek meghatározásához. Az alábbiakban mindegyik funkciót mező jelöli, és hozzárendel egy adattípust, valamint ha hiányzik-e értékeket. Itt található a különböző változótípusok teljes listája, amelyeket itt használhat, de az egyszerűség kedvéért most egy karakterlánccal fogunk ragaszkodni.

Nem fogok minden oszlopot használni a párhuzamosság meghatározására, de ha úgy gondolja, hogy ez megkönnyíti az adatkeretben található értékek azonosítását.

# Állítsa be a mezőket
mezők = [
        {'mező': 'Forrás', 'típus': 'Set'},
        {'mező': 'webhely neve', 'típus': 'karakterlánc'},
        {'mező': 'Cím', 'típus': 'String'},
        {'mező': 'Zip', 'type': 'Pontos', 'hiányzik': True},
        {'mező': 'Telefon', 'type': 'String', 'hiányzik': True},
        {'mező': 'E-mail cím', 'típus': 'Karakterlánc', 'hiányzik': Igaz},
        ]

Most kezdjük el adagolni dedupciós adatokat.

# Jelölje be modellünket
deduper = dedupe.Dedupe (mezők)
# Ellenőrizze, hogy működik-e
deduper
>>
# Töltsön be néhány minta adatot ... 15000 rekordba
deduper.sample (df, 15000)

Most folytatjuk a címkézést. Amikor ezt a módszert futtatja az alábbiakban, dedupe kéri, hogy végezzen néhány egyszerű címkézést.

dedupe.consoleLabel (deduper)
Amit látnod kell; a levezető manuális kiképzése

Az igazi „ha!” Pillanat az, amikor megkapja ezt a parancsot. Ez a deduktor arra kéri, hogy edzje meg, tehát tudja, mit kell keresnie. Tudod, hogy egy ismétlődő értéknek hogyan kell kinéznie, ezért csak adja át ezt a tudást.

Ugyanazra vonatkoznak ezek a nyilvántartások?
(y) es / (n) o / (u) nsure / (f) kiválik

Most már nem kell több tonna és tonna nyilvántartást keresnie, hogy megnézhesse, van-e másolás. Ehelyett egy ideghálót oktat, hogy duplikákat találjon az adatkeretben.

Miután megadta némi címkéjét, fejezze be az edzési folyamatot, és mentse el az előrehaladását. Később visszatérhet az ideghálóhoz, ha találja megismételt dataframe-objektumokat, amelyeket levonni kell.

deduper.train ()
# Képzés mentése
tf-vel nyitva (training_file, 'w'):
        deduper.writeTraining (TF)
# Beállítások mentése
with open (settings_file, 'wb') mint sf:
        deduper.writeSettings (sf)

Már majdnem készen vagyunk, mivel következőként küszöböt kell beállítanunk az adatainkhoz. Ha a visszahívási súly 1-gyel egyenlő, akkor azt mondjuk, hogy a deducer a visszahívás pontosságát és a pontosságot is értékeli. Ha azonban a visszahívási súly = 3, akkor háromszor annyit értékelnénk. Ezekkel a beállításokkal játszhat, hogy megnézze, mi működik a legjobban.

küszöbérték = deduper.küszöb (df, visszahívási súly = 1)

Végül megkereshetjük a df-et, és megnézhetjük, hol vannak a másolatok. Hosszú ideje eljutottunk erre a pozícióra, de ez sokkal jobb, mint ezt kézzel elvégezni.

# Csoportosítsa össze a másolatokat
clustered_dupes = deduper.match (data_d, küszöbérték)
print ('Van {} másolatkészlet' .formátum (len (fürtözött_alak)))

Vessen egy pillantást a másolatokra.

clustered_dupes
>>
[((0, 1, 215, 509, 510, 1225, 1226, 1879, 2758, 3255),
  tömb ([0.88552043, 0.88552043, 0.77351897, 0.88552043, 0.88552043,
         0.88552043, 0.88552043, 0.89765924, 0.75684386, 0.83023088])),
 ((2, 3, 216, 511, 512, 1227, 1228, 2687), ...

Hum, ez nem sokat mond nekünk. Valójában mi mutatja meg nekünk? Mi történt minden értékünkkel?

Ha közelebbről megnézzük az értékeket (0, 1, 215, 509, 510, 1225, 1226, 1879, 2758, 3255), akkor a párhuzamos példányok azonosító helyei a deduktor szerint azt gondolják, hogy valójában ugyanaz az érték. És ellenőrizhetjük az eredeti adatokat.

{'Id': '215',
 'Forrás': 'cps_early_childhood_portal_scrape.csv',
 „Telek neve”: „üdvösségsereg temploma”,
 „Cím”: „1 n. Ogden”
...
{'Id': '509',
 'Forrás': 'cps_early_childhood_portal_scrape.csv',
 „Telephely neve”: „üdvözítő hadsereg - templom / üdvözítő hadsereg”,
 „Cím”: „1 n ogden ave”,
 „Zip”: Nincs,
..

Ez nekem másolatoknak tűnik. Szép.

A deducer sokkal fejlettebb alkalmazásai vannak, például a matchBlocks a klaszterek sorozatához vagy az Interaction mezők, ahol a két mező közötti interakció nem csupán additív, hanem multiplikatív. Ezt már nagyon sokat kellett átvinnem, ezért ezt a magyarázatot a fenti cikkhez hagyom.

Karakterlánc megegyezik a fuzzywuzzy-val

Próbáld ki ezt a könyvtárat. Nagyon érdekes, mert pontszámot ad arról, milyen közel vannak a vonósok összehasonlításukhoz.

Ez egy elképesztően nagyszerű eszköz, mivel a múltban olyan projektekkel foglalkoztam, amelyekben a Google Sheet fuzzymatch kiegészítésére kellett támaszkodnom az adatok érvényesítésével kapcsolatos problémák diagnosztizálására - gondolom, hogy a CRM szabályokat nem alkalmazták, vagy megfelelően cselekedtek - és meg kellett tisztítaniuk rekordok bármilyen elemzés elvégzéséhez.

De a nagy adatkészletek esetében ez a megközelítés teljesen megsemmisül.

A fuzzywuzzy használatával azonban tudományos kérdésben elkezdhet belemenni a húr illesztésbe. Nem túl technikai, de összehasonlításkor Levenshtein távolságnak nevezik. Ez egy karakterlánc-hasonlósági mutató két szekvencia számára, úgy, hogy a távolság az egy karakter szerkesztések száma, amely ahhoz szükséges, hogy az egyik szót a másikra változtassuk.

Például. Ha meg akarja változtatni a foo karakterláncot sávra, akkor a változtatható karakterek minimális száma 3 legyen, és ezt használják a „távolság” meghatározására.

Lássuk, hogyan működik ez a gyakorlatban.

$ pip3 install fuzzywuzzy
# test.py
a fuzzywuzzy import fuzz-ból
a fuzzywuzzy importálási folyamatból
foo = 'ez a karakterlánc'
bar = 'tetszik ez a húr?'
fuzz.ratio (foo, bár)
>>
71
fuzz.WRatio (foo, bar) # Súlyozott arány
>>
73
fuzz.UQRatio (foo, bar) # Unicode gyors arány
>> 73

A fuzzywuzzy csomagnak különféle módjai vannak a vonóságok értékelésére (WRatio, UQRatio stb.), És csak ragaszkodni fogok ehhez a cikkhez tartozó szabványos megvalósításhoz.

Ezután megvizsgálhatunk egy tokenizált karakterláncot, amely visszaadja a szekvenciák 0 és 100 közötti hasonlóságának mértékét, de összehasonlítás előtt rendezi a tokent. Ez kulcsfontosságú, mivel valószínűleg csak a húrok tartalmát, nem pedig azok pozícióját szeretné megtekinteni.

A foo és a bar húrok azonos jelölésekkel rendelkeznek, de szerkezetileg különböznek. Szeretné ugyanúgy bánni velük? Most már könnyedén megnézheti és figyelembe veheti az ilyen típusú különbségeket adatai között.

foo = 'ez foo'
bar = 'foo a'
fuzz.ratio (foo, bár)
>>
31
fuzz.token_sort_ratio ('ez egy foo', 'foo a ez')
>>
100

Vagy ezt követően meg kell találnia a karakterlánc legközelebbi egyezését az értékek listájából. Ebben az esetben a Harry Potter címeket fogjuk vizsgálni.

Mi a helyzet azzal a Harry Potter könyvvel, amelynek… valami címe… van… nem akarom. Csak azt kell kitalálnom, hogy megtudjam, melyik könyv pontszáma áll legközelebb a tippemhez.

Azt hiszem, hogy „tűz”, nézzük meg, hogyan viszonyul a pontozás a lehetséges címek listájához.

lst_to_eval = ['Harry Potter és a filozófus kő',
'Harry Potter és a titkok kamrája',
„Harry Potter és az Azkabani fogoly”,
'Harry Potter és a Tűz Serlege',
„Harry Potter és a Főnix Rendje”,
'Harry Potter és a félvér herceg',
'Harry Potter és a Halál Ereklyéi']
# A két legjobb válasz a feltételezésem alapján
process.extract ("tűz", lst_to_eval, limit = 2)
>>
[('Harry Potter és a tűz serlege', 60), ("Harry Potter és a varázslókő", 30)
eredmények = folyamat.extract ("tűz", lst_to_eval, limit = 2)
az eredmények eredményéhez:
  print ('{}: értéke {}'. formátum (eredmény [0], eredmény [1]))
>>
Harry Potter és a Tűz Serlege: pontszáma 60
Harry Potter és a varázslókő: 30 pontszámot szerez

Vagy ha csak vissza szeretne adni, akkor megteheti.

>>> process.extractOne ("kő", lst_to_eval)
("Harry Potter és a varázslókő", 90)

Tudom, hogy korábban beszéltünk a dedupe-ről, de itt van egy másik alkalmazás ugyanezen eljáráshoz a fuzzywuzzy-val. Vehetünk egy listát a karakterláncokat tartalmazó másolatokról, és fuzzy egyezést használunk a másolatok azonosítására és eltávolítására.

Nem olyan divatos, mint egy idegháló, de a kis műveleteknél is elvégzi a munkát.

Folytatjuk a Harry Potter témát, és keresünk párhuzamos karaktereket a könyvekből egy listán belül.

A küszöbértéket 0 és 100 között kell beállítania. Mivel a küszöb csökken, a talált másolatok száma növekszik, tehát a visszatérő lista rövid lesz. Az alapértelmezett érték 70.

# Ismétlődő karakterek neve
Satur_dupes = [
'Harry Potter',
„H. Fazekas',
„Harry James Potter”,
„James Potter”,
"Ronald Bilius \" Ron \ "Weasley",
„Ron Weasley”,
'Ronald Weasley']
# Nyomtassa ki az ismétlődő értékeket
process.dedupe (contains_dupes)
>>
dict_keys (['Harry James Potter', 'Ronald Bilius' Ron 'Weasley'])
# Nyomtassa ki az ismétlődő értékeket magasabb küszöbértékkel
process.dedupe (tartalmaz_dupes, küszöbérték = 90)
>>
dict_keys (['Harry James Potter', 'H. Potter', 'Ronald Bilius' Ron 'Weasley'))

Gyors bónuszként fuzzy párosítást is végezhet a datetime csomaggal, hogy dátumot vonjon ki a szövegből. Ez nagyszerű, ha nem akarja (újra) írni regex kifejezést.

a dateutil.parser behozatali elemzéséből
dt = elemezni ("Ma 2047. január 1., 8:21:00 van, homályos = igaz)
print (dt)
>>
2047-01-01 08:21:00
dt = elemezni ("2049. május 18. valami valami", homályos = igaz)
print (dt)
>>
2049-05-18 00:00:00

Próbálj ki néhány sklearn-t

Az adatok megtisztításával együtt el kell készítenie az adatokat is, hogy olyan formában legyen, amelyet beilleszthet a modelljébe. Az itt bemutatott példák többsége közvetlenül a dokumentációból származik, amelyet ki kell ellenőrizni, mivel ez valóban jó feladat, hogy jobban megmagyarázza az egyes csomagok újszerűségét.

Először az előfeldolgozó csomagot importáljuk, majd további módszereket kapunk onnan, ahogyan megyünk. Ezenkívül a sklearn 0.20.0 verziót is használom, tehát ha problémái vannak néhány csomag importálásával, ellenőrizze a verzióját.

Két különféle típusú adatokkal fogunk dolgozni, az str és az int csak azért, hogy kiemeljük a különféle előfeldolgozási technikák működését.

# A projekt elején
a sklearn import előfeldolgozásából
# És készítsünk egy véletlenszerű tömböt a feldolgozáshoz
ary_int = np.random.randint (-100, 100, 10)
ary_int
>> [5, -41, -67, 23, -53, -57, -36, -25, 10, 17]
# És néhány munkával együtt dolgozni
ary_str = ['foo', 'bar', 'baz', 'x', 'y', 'z']

Próbáljunk meg néhány gyors címkézést a LabelEncoder segítségével az ary_str oldalon. Ez azért fontos, mert nemcsak a nyers karakterláncokat tudja betáplálni - jól tudod, de ez a cikk hatókörén kívül esik - a modellekben is. Tehát minden egyes karakterláncot kódolunk, 0 és n közötti értékkel. Az ary_str-ben 6 egyedi érték van, tehát tartományunk 0 - 5 lenne.

a sklearn.preprocessing import LabelEncoder programjából
l_encoder = előfeldolgozás.LabelEncoder ()
l_encoder.fit (ary_str)
>> LabelEncoder ()
# Milyen értékeink vannak?
l_encoder.transform ([ 'ize'])
>> tömb ([2])
l_encoder.transform ([ 'BAZ'])
>> tömb ([1])
l_encoder.transform ([ 'bar'])
>> tömb ([0])

Észre fogja venni, hogy ezeket nem rendelik meg, mivel még a foo-n keresztül is jött, mielőtt a tömb sávját 2-re kódolták, míg a sávot az 1-re kódolták. Másik kódolási módszert fogunk használni, amikor ellenőriznünk kell az értékeink kódolását. a megfelelő sorrendben.

Ha sok kategóriája van a nyomon követése érdekében, akkor elfelejtheti, hogy melyik térképet melyik int. Ehhez létrehozhatunk egy szót.

# Ellenőrizze a leképezéseket
lista (l_encoder.classes_)
>> ['bár', 'baz', 'foo', 'x', 'y', 'z']
# Készítsen leképezési szótárt
dict (zip (l_encoder.classes_, l_encoder.transform (l_encoder.classes_))))
>> {'bár': 0, 'baz': 1, 'foo': 2, 'x': 3, 'y': 4, 'z': 5}

A folyamat kissé eltér, ha van adatkerete, de valójában egy kicsit könnyebb. Csak alkalmaznia kell () a LabelEncoder objektumot a DataFrame-hez. Minden oszlophoz egyedi címkét kap az oszlop értékeihez. Figyelje meg, hogy a foo hogyan kódolódik 1-hez, de ugyanúgy, mint az y.

# Próbálja ki a LabelEncoder alkalmazást egy adatkeretben
importálj pandákat pd-ként
l_encoder = preprocessing.LabelEncoder () # Új objektum
df = pd.DataFrame (data = {'col1': ['foo', 'bar', 'foo', 'bar'],
                          'col2': ['x', 'y', 'x', 'z'],
                          'col3': [1, 2, 3, 4]})
# Most az egyszerű részért
df.apply (l_encoder.fit_transform)
>>
   col1 col2 col3
0 1 0 0
1 0 1 1
2 1 0 2
3 0 2 3

Most továbblépünk a szokásos kódoláshoz, ahol a funkciókat még mindig egész számként fejezzük ki, de a hely és a szerkezet értelmezése megmutatkozik. Olyan, hogy x y előtt, y előtt z előtt áll.

Csavarkulcsot viszünk ide. Nem csak az értékek vannak rendezve, hanem párosítva is vannak egymással.

Kétféle értéket fogunk venni: „foo”, „bar”, „baz” és [„x”, „y”, „z”]. Ezután a tömbök minden egyes értékkészletére 0, 1 és 2 kódolást készítünk, és mindegyik értékhez kódolt párt hozunk létre.

Például. ['Foo', 'z'] -ot [0, 2] -re és ['baz', 'x'] -re leképezik [2, 0] -ra.

Ez egy jó megközelítés akkor, ha egy csoportot kell elfoglalnia, és regresszióra rendelkezésre kell bocsátania, és különösen akkor jó, ha egymással átfedő karakterlánckészletek vannak - külön kategóriák, amelyek még mindig átfedésben vannak -, és ábrázolásra szorul az adatkeretben. .

sklearn.preprocessing import OrdinalEncoder-ből
o_encoder = OrdinalEncoder ()
ary_2d = [['foo', 'bar', 'baz'], ['x', 'y', 'z']]
o_encoder.fit (2d_ary) # Helyezze be az értékeket
o_encoder.transform ([['foo', 'y']])
>> tömb ([[0., 1.]])

A klasszikus forró vagy 'dummy' kódolás, ahol a kategóriák egyedi jellemzőit ezután 0 vagy 1 s további oszlopként fejezik ki, attól függően, hogy az érték megjelenik-e vagy sem. Ez a folyamat minden kategóriához bináris oszlopot hoz létre, és egy ritka mátrixot vagy sűrű tömböt ad vissza.

Hitel a https://blog.myyellowroad.com/ oldalon

Miért is használja ezt? Mivel az ilyen típusú kódolásra szükség van kategorikus adatok adagolására sok scikit modellhez, például lineáris regressziós modellekhez és SVM-ekhez. Tehát nyugodtan ezt.

a sklearn.preprocessing importáló OneHotEncoderből
hot_encoder = OneHotEncoder (handle_unknown = 'ignore')
hot_encoder.fit (ary_2d)
hot_encoder.categories_
>>
[tömb (['foo', 'x'], dtype = object), tömb (['bar', 'y'], dtype = object), tömb (['baz', 'z'], dtype = object )]
hot_encoder.transform ([['foo', 'foo', 'baz'], ['y', 'y', 'x']]). toarray ()
>>
tömb ([[1., 0., 0., 0., 1., 0.],
       [0., 0., 0., 1., 0., 0.]])

Mi lenne, ha dolgozzunk adatkerettel?

Használhatunk még egy forró kódolást? Valójában sokkal könnyebb, mint gondolnád, mivel csak a pandákhoz tartozó .get_dummies () fájlt kell használnod.

pd.get_dummies (df)
      col3 col1_bar col1_foo col2_x col2_y col2_z
0 1 0 1 1 0 0
1 2 1 0 0 1 0
2 3 0 1 1 0 0
3 4 1 0 0 0 1

A df három oszlopának kettő fel van osztva, és binárisan kódolva egy adatkeret.

Például. a col1_bar oszlop a colt a df-től, de rekordértékként 1 van, amikor a bar az eredeti adatkeretben volt.

Mi lenne, ha szolgáltatásainkat egy bizonyos tartományon belül átalakítani kell? A MinMaxScaler használatával minden funkció külön-külön méretezhető úgy, hogy az a megadott tartományban legyen. Alapértelmezés szerint az értékek 0 és 1 között vannak, de megváltoztathatja a tartományt.

a sklearn.preprocessing import MinMaxScaler programból
mm_scaler = MinMaxScaler (feature_range = (0, 1)) # 0 és 1 között
mm_scaler.fit ([ary_int])
>> MinMaxScaler (copy = True, feature_range = (0, 1))
print (scaler.data_max_)
>> [5. -41. -67. 23. -53. -57. -36. -25. 10. 17.]
Nyomtatás (mm_scaler.fit_transform ([ary_int]))
>> [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] # Hmm, valami nincs rendben

Ha észreveszi, hogy a kimenet minden nulla, ami nem az, amit akartunk. Jó magyarázat van itt és itt, hogy miért történt ez, de a rövid történet az, hogy a tömb helytelenül van formázva.

Ez egy (1, n) mátrix, amelyet konvertálni kell (n, 1) mátrixszá. Ennek legegyszerűbb módja annak ellenőrzése, hogy a tömb egy számtalan tömb, így képes az alak kezelésére.

# Hozzon létre számtalan tömböt
ary_int = np.array ([5, -41, -67, 23, -53, -57, -36, -25, 10, 17])
# Átalakítás
mm_scaler.fit_transform (ary_int [:, np.newaxis])
>>
tömb ([[0.8],
       [0.28888889],
       [0. ],
       [1. ],
       [0.15555556],
       [0.11111111],
       [0.34444444],
       [0.46666667],
       [0.85555556],
       [0.93333333]])
# Használhatja is
mm_scaler.fit_transform (ary_int.reshape (-1, 1))
# Próbáljon ki egy másik skálát is
mm_scaler = MinMaxScaler (feature_range = (0, 10))
mm_scaler.fit_transform (ary_int.reshape (-1, 1))
>>
tömb ([[8.],
       [2.88888889],
       [0.],
       [10. ],
       [1.55555556],
       [1.11111111],
       [3.44444444],
       [4.66666667],
       [8.55555556],
       [9.33333333]])

Most, hogy gyorsan méretezhetjük adatainkat, mi lenne, ha valamilyen formát alkalmaznánk az átalakított adatokhoz? Megvizsgáljuk az adatok egységesítését, amely olyan értékeket fog adni, amelyek 0 és 1 közötti átlaggal rendelkező gauss-értéket hoznak létre. Ezt a megközelítést fontolóra veheti a gradiens leszállás végrehajtásakor, vagy ha súlyozott bemenetekre van szükség, például regresszió és idegi hálózatok. Továbbá, ha KNN-t fog végrehajtani, először méretezze meg adatait. Vegye figyelembe, hogy ez a megközelítés eltér a normalizálástól, ezért ne tévessze össze.

Egyszerűen használja az előfeldolgozás skáláját.

preprocessing.scale (foo)
>> tömb ([0.86325871, -0.58600774, -1.40515833, 1.43036297, -0.96407724, -1.09010041, -0.42847877, -0.08191506, 1.02078767, 1.24132821])
preprocessing.scale (ize) .mean ()
>> -4.4408920985006264e-17 # Alapvetően nulla
 preprocessing.scale (ize) .std ()
>> 1.0 # Pontosan mit akartunk

Az utolsó sklearn-csomag, amelyet meg kellett nézni, a Binarizer. Még mindig 0-t és 1-et kapsz ezen keresztül, de most a saját feltételeid vannak meghatározva. Ez a numerikus szolgáltatások küszöbének meghatározása a logikai értékek megszerzéséhez. A küszöbértéket meghaladó értékek küszöbértéke 1-re térképezik, míg ≤ értékek 0-ra térképeznek. Ez szintén egy általános folyamat, amikor a szöveg előfeldolgozza a terminusfrekvenciákat a dokumentumon vagy a korpuszon belül.

Ne feledje, hogy mind a fit (), mind a transform () egy 2d tömböt igényel, ezért beágyaztam az ary_int-t egy másik tömbbe. Ebben a példában a küszöbértéket -25-re tettem, tehát minden szigorúan feletti számhoz 1-et kapunk.

származó sklearn.preprocessing import Binarizer programból
# -25
tz = Binarizer (küszöb = -25,0) .fit ([ary_int])
tz.transform ([ary_int])
>> tömb ([[1, 0, 0, 1, 0, 0, 0, 0, 1, 1]])

Most, hogy rendelkezünk ezzel a néhány különböző technikával, melyik a legjobb az algoritmusához? Valószínűleg a legjobb, ha néhány különböző közbenső adatkeretet ment, méretezött adatokkal, összesített adatokkal stb., Így láthatja a modell (k) outputjára gyakorolt ​​hatást.

Végső gondolatok

Az adatok tisztítása és előkészítése elkerülhetetlen, és általában hálátlan feladat az adattudomány területén. Ha szerencséje van, hogy veled van egy adatmérnöki csapat, aki segítséget nyújthat az ETL-csővezetékek felállításában a munka megkönnyítése érdekében, akkor az adattudósok kisebbségében lehet.

Az élet nem csak egy csomó Kaggle adatkészlet, ahol a valóságban döntéseket kell hoznia arról, hogyan férhet hozzá és törölheti a mindennapi adatokhoz. Időnként nagyon sok idő van arra, hogy megbizonyosodjon arról, hogy minden a megfelelő helyen van, de a válaszadásra legtöbbször rákényszerül. Ha rendelkeznek a megfelelő eszközökkel, és megérti a lehetséges lehetőségeket, könnyen megismerheti ezeket a válaszokat.

Mint mindig, remélem, megtanultál valami újat.

Egészségére,

További olvasmány