2016-05-06 6 views
6

So. Wir haben eine chaotisch Daten in einer TSV-Datei gespeichert, die ich analysieren muss. Dies ist, wie esPython. Pandas. Große Daten. Unordentliche TSV-Datei. Wie man die Daten streitet?

sieht
status=200 protocol=http region_name=Podolsk datetime=2016-03-10 15:51:58 user_ip=0.120.81.243 user_agent=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36 user_id=7885299833141807155 user_vhost=tindex.ru method=GET page=/search/ 

Und das Problem ist, dass einige der Reihen unterschiedliche Spaltenreihenfolge haben/einige von ihnen Werte fehlen, und ich muss mit hoher Leistung, dass loszuwerden (da die Datensätze Ich arbeite mit bis zu 100 Gigabyte).

Data = pd.read_table('data/data.tsv', sep='\t+',header=None,names=['status', 'protocol',\ 
                'region_name', 'datetime',\ 
                'user_ip', 'user_agent',\ 
                'user_id', 'user_vhost',\ 
                'method', 'page'], engine='python') 
Clean_Data = (Data.dropna()).reset_index(drop=True) 

Jetzt habe ich die fehlende Werte zu befreien, aber ein Problem bleibt nach wie vor! Dies ist, wie die Daten aussehen: enter image description here

Und das ist, wie das Problem aussieht: enter image description here

Wie Sie einige Spalten zu sehen sind, ausgeglichen werden. Ich habe eine sehr niedrige Leistungslösung

ids = Clean_Data.index.tolist() 
for column in Clean_Data.columns: 
    for row, i in zip(Clean_Data[column], ids): 
     if np.logical_not(str(column) in row): 
      Clean_Data.drop([i], inplace=True) 
      ids.remove(i) 

So, jetzt die Daten sehen gut aus ... zumindest ich kann mit ihm arbeiten! Aber was ist die High-Performance ALTERNATIVE zu der Methode, die ich oben gemacht habe?

-Update auf unutbu Code: Zurückverfolgungsfehler

--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-4-52c9d76f9744> in <module>() 
     8  df.index.names = ['index', 'num'] 
     9 
---> 10  df = df.set_index('field', append=True) 
    11  df.index = df.index.droplevel(level='num') 
    12  df = df['value'].unstack(level=1) 

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/core/frame.pyc in set_index(self, keys, drop, append, inplace, verify_integrity) 
    2805    if isinstance(self.index, MultiIndex): 
    2806     for i in range(self.index.nlevels): 
-> 2807      arrays.append(self.index.get_level_values(i)) 
    2808    else: 
    2809     arrays.append(self.index) 

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/multi.pyc in get_level_values(self, level) 
    664   values = _simple_new(filled, self.names[num], 
    665        freq=getattr(unique, 'freq', None), 
--> 666        tz=getattr(unique, 'tz', None)) 
    667   return values 
    668 

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/range.pyc in _simple_new(cls, start, stop, step, name, dtype, **kwargs) 
    124     return RangeIndex(start, stop, step, name=name, **kwargs) 
    125    except TypeError: 
--> 126     return Index(start, stop, step, name=name, **kwargs) 
    127 
    128   result._start = start 

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/base.pyc in __new__(cls, data, dtype, copy, name, fastpath, tupleize_cols, **kwargs) 
    212    if issubclass(data.dtype.type, np.integer): 
    213     from .numeric import Int64Index 
--> 214     return Int64Index(data, copy=copy, dtype=dtype, name=name) 
    215    elif issubclass(data.dtype.type, np.floating): 
    216     from .numeric import Float64Index 

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/numeric.pyc in __new__(cls, data, dtype, copy, name, fastpath, **kwargs) 
    105    # with a platform int 
    106    if (dtype is None or 
--> 107      not issubclass(np.dtype(dtype).type, np.integer)): 
    108     dtype = np.int64 
    109 

TypeError: data type "index" not understood 

Pandas Version: 0.18.0-np110py27_0

aktualisieren

Alles funktionierte ... Vielen Dank allen!

Antwort

5

Angenommen, Sie wie dem TSV-Daten haben:

status=A protocol=B region_name=C datetime=D user_ip=E user_agent=F user_id=G 
user_id=G status=A region_name=C user_ip=E datetime=D user_agent=F protocol=B 
protocol=B  datetime=D status=A user_ip=E user_agent=F user_id=G 

Die Reihenfolge der Felder scambled werden kann, und es kann sein, fehlende Werte. Sie müssen jedoch keine Zeilen löschen, nur weil die Felder nicht in einer bestimmten Reihenfolge angezeigt werden. Sie können die Feldnamen verwenden, die in den Zeilendaten selbst enthalten sind, um die Werte in den richtigen Spalten zu platzieren.Zum Beispiel

import pandas as pd 

df = pd.read_table('data/data.tsv', sep='\t+',header=None, engine='python') 
df = df.stack().str.extract(r'([^=]*)=(.*)', expand=True).dropna(axis=0) 
df.columns = ['field', 'value'] 

df = df.set_index('field', append=True) 
df.index = df.index.droplevel(level=1) 
df = df['value'].unstack(level=1) 

print(df) 

field datetime protocol region_name status user_agent user_id user_ip 
index                 
0   D  B   C  A   F  G  E 
1   D  B   C  A   F  G  E 
2   D  B  None  A   F  G  E 

Um eine große TSV-Datei zu behandeln ergibt, können Sie Zeilen in Blöcken verarbeiten, und dann verketten die verarbeiteten Stücke in einen Datenrahmen am Ende:

import pandas as pd 

chunksize =  # the number of rows to be processed per iteration 
dfs = [] 
reader = pd.read_table('data/data.tsv', sep='\t+',header=None, engine='python', 
         iterator=True, chunksize=chunksize) 
for df in reader: 
    df = df.stack().str.extract(r'([^=]*)=(.*)', expand=True).dropna(axis=0) 
    df.columns = ['field', 'value'] 
    df.index.names = ['index', 'num'] 

    df = df.set_index('field', append=True) 
    df.index = df.index.droplevel(level='num') 
    df = df['value'].unstack(level=1) 
    dfs.append(df) 

df = pd.concat(dfs, ignore_index=True) 
print(df) 

Erläuterung: Gegeben df:

In [527]: df = pd.DataFrame({0: ['status=A', 'user_id=G', 'protocol=B'], 
1: ['protocol=B', 'status=A', 'datetime=D'], 
2: ['region_name=C', 'region_name=C', 'status=A'], 
3: ['datetime=D', 'user_ip=E', 'user_ip=E'], 
4: ['user_ip=E', 'datetime=D', 'user_agent=F'], 
5: ['user_agent=F', 'user_agent=F', 'user_id=G'], 
6: ['user_id=G', 'protocol=B', None]}); df 
    .....: .....: .....: .....: .....: .....: .....: 
Out[527]: 
      0   1    2   3    4    5   6 
0 status=A protocol=B region_name=C datetime=D  user_ip=E user_agent=F user_id=G 
1 user_id=G status=A region_name=C user_ip=E datetime=D user_agent=F protocol=B 
2 protocol=B datetime=D  status=A user_ip=E user_agent=F  user_id=G  None 

Sie alle Werte in eine einzige Spalte

In [449]: df.stack() 
Out[449]: 
0 0   status=A 
    1  protocol=B 
    2 region_name=C 
    3  datetime=D 
    4  user_ip=E 
    5  user_agent=F 
    6  user_id=G 
1 0  user_id=G 
    1   status=A 
    2 region_name=C 
    3  user_ip=E 
    4  datetime=D 
    5  user_agent=F 
    6  protocol=B 
2 0  protocol=B 
    1  datetime=D 
    2   status=A 
    3  user_ip=E 
    4  user_agent=F 
    5  user_id=G 
dtype: object 

und dann anwenden .str.extract(r'([^=]*)=(.*)') verschmelzen können die Feldnamen aus dem Wert zu trennen:

In [450]: df = df.stack().str.extract(r'([^=]*)=(.*)', expand=True).dropna(axis=0); df 
Out[450]: 
       0 1 
0 0  status A 
    1  protocol B 
    2 region_name C 
    3  datetime D 
    4  user_ip E 
    5 user_agent F 
    6  user_id G 
1 0  user_id G 
    1  status A 
    2 region_name C 
    3  user_ip E 
    4  datetime D 
    5 user_agent F 
    6  protocol B 
2 0  protocol B 
    1  datetime D 
    2  status A 
    3  user_ip E 
    4 user_agent F 
    5  user_id G 

Um es einfacher Um Teile des DataFrame zu referenzieren, geben wir den Spalten und Indexebenen beschreibende Namen:

In [530]: df.columns = ['field', 'value']; df.index.names = ['index', 'num']; df 
Out[530]: 
       field value 
index num     
0  0   status  A 
     1  protocol  B 
... 

Nun, wenn wir die field Spalte in den Index zu verschieben:

In [531]: df = df.set_index('field', append=True); df 
Out[531]: 
         value 
index num field    
0  0 status   A 
     1 protocol  B 
     2 region_name  C 
     3 datetime  D 
... 

und legen Sie die num Indexstand:

In [532]: df.index = df.index.droplevel(level='num'); df 
Out[532]: 
        value 
index field    
0  status   A 
     protocol  B 
     region_name  C 
     datetime  D 
... 

dann können wir einen Datenrahmen der gewünschten Form erhalten durch Verschieben der field Indexstufe in den Spaltenindex:

In [533]: df = df['value'].unstack(level=1); df 
Out[533]: 
field datetime protocol region_name status user_agent user_id user_ip 
index                 
0   D  B   C  A   F  G  E 
1   D  B   C  A   F  G  E 
2   D  B  None  A   F  G  E 
+0

Könnten Sie bitte Ihren Code kommentieren, damit ich (und jeder) Ihnen vollständig folgen kann? Hm es scheint, dass iPython mit diesem Code wirklich langsam arbeitet. Tatsächlich ist es bei 'df = df ['all'] hängen geblieben. Str.extract ('\ t'.join ([' (. *) '] * (N + 1)), expand = True)' Zeile .. . irgendwelche Ideen? –

+0

Ich habe den Code geändert (so dass er diese Zeile nicht mehr verwendet) und einen Abschnitt hinzugefügt, wie man eine TSV-Datei in Chunks liest. Wenn die Chunksize nicht zu groß ist, sollten Sie in der Lage sein, Ergebnisse schneller zu sehen. Außerdem müssen Sie möglicherweise keinen riesigen Datenrahmen bilden. vielleicht können Sie den TSV iterativ in Blöcken verarbeiten. – unutbu

+0

'df = df.set_index ('Feld', append = True)' throws 'TypeError: Datentyp" index "nicht verstanden' ... Sie können meine [Datendatei (nur 30Mb)] ausprobieren (https: // drive.google.com/open?id=0BxWmwAIo1D_nUUpqTU5TdDlaUHc) ... Eigentlich habe ich keine Ahnung warum. Scheint ein Pandas Bug zu sein –

4

Sie können mit Pandas' vectorized string operations, speziell str.contains:

import numpy as np 

# boolean index of rows to keep 
is_valid = np.ones(Clean_data.shape[0], np.bool) 

for column in Clean_Data.columns: 

    # check whether rows contain this column name 
    is_valid &= Clean_Data[column].str.contains(column) 

# drop rows where not all elements contain their respective column names 
Clean_Data.drop(np.where(~is_valid)[0], inplace=True) 
+0

'---------------------------------------------- ----------------------------- ValueError Traceback (letzter Aufruf zuletzt) ​​ in () 8 # prüfen, ob Zeilen diesen Spaltennamen enthalten ----> 9 is_valid & = Clean_Data [Spalte] .str.contains (column) 11 # drop rows wo nicht alle Elemente die entsprechenden Spaltennamen enthalten ValueError: Operanden konnten nicht zusammen mit shapes gesendet werden (10,) (75618,) (10,) ' –

+0

Sorry, da war ein Tippfehler bei der Initialisierung von 'is_valid'. Versuche es jetzt. –

3

Ich kann keine Kommentare hinzufügen, also poste ich dies als Antwort (In Wirklichkeit ist es ein Kommentar als Antwort auf Ihren Kommentar über Speicherverbrauch und Laufzeit).

Eine Sache, die Sie für große Dateien (100 GB) beachten müssen, ist, dass Sie diese Dateien nicht in den Speicher lesen werden. Sie können die Chunk-Größe für Pandas “Large data” work flows using pandas oder How to read a 6 GB csv file with pandas einstellen, oder verwenden Sie den Yield-Generator mit dem CSV-Modul und lesen Sie die Dateien Zeile/Zeile für Zeile. Reading a huge .csv in python

Einbeziehung @ unutbu Kommentar über regex die Einträge in Spalten gegeben zu sortieren, dass die Feldnamen sind so eindeutig für jede Zelle abgegrenzt (dh r'(.*)=(.*)' ist alles, was erforderlich ist - obwohl es einige Fehlerkorrektur erforderlich sein) sollte alles, was Sie brauchen (auch, wie Sie sagen, das Löschen ganzer Zeilen wegen fehlender Daten ist kein typischer oder empfohlener Ansatz).