2016-05-06 45 views
6

Così. Abbiamo un disordinato dati memorizzati in un file TSV che ho bisogno di analizzare. Questo è come apparePython. Pandas. BigData. File TSV disordinato. Come sballare i dati?

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/ 

E il problema è che alcune delle righe sono l'ordine delle colonne diverso/alcuni di loro valori mancanti e ho bisogno di sbarazzarsi di quella ad alte prestazioni (dal momento che i set di dati sto lavorando con sono fino a 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) 

Ora mi sono liberato di valori mancanti ma un problema rimane ancora! Questo è come i dati appare: enter image description here

Ed è così che il problema si presenta: enter image description here

Come potete vedere alcune delle colonne sono sfalsati. Ho fatto una soluzione molto basse prestazioni

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) 

Così ora i dati sembra buono ... almeno posso lavorare con lui! Ma qual è l'ALTERNATIVA ad alte prestazioni rispetto al metodo che ho fatto sopra?

aggiornamento sul codice unutbu: errore di traceback

--------------------------------------------------------------------------- 
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 versione: 0.18.0-np110py27_0

Aggiornamento

Tutto ha funzionato ... Grazie a tutti!

risposta

5

Supponete di avere i dati TSV come questa:

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 

L'ordine dei campi può essere scambled, e ci possono essere valori mancanti. Tuttavia, non è necessario eliminare righe solo perché i campi non vengono visualizzati in un determinato ordine. È possibile utilizzare i nomi dei campi forniti nei dati della riga stessa per posizionare i valori nelle colonne corrette.Ad esempio,

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) 

cede

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 

Per gestire un file di grandi dimensioni TSV, si potrebbe elaborare file in blocchi, e quindi concatenare i pezzi lavorati in una sola dataframe alla fine:

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) 

Spiegazione: Data 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 

si possono confluire tutti i valori in una singola colonna

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 

e quindi applicare .str.extract(r'([^=]*)=(.*)') per separare il nome del campo dal valore:

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 

Per facilitare per fare riferimento a parti di DataFrame, diamo i nomi descrittivi di colonne e indici:

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 
... 

Ora, se si passa la colonna field nell'indice:

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 
... 

e cadere il livello num index:

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 
... 

allora possiamo ottenere una dataframe della forma desiderata da spostando il livello dell'indice field nell'indice della colonna:

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

Potresti commentare il tuo codice così io (e tutti) posso seguirti completamente? Sembra che iPython lavori veramente lento con quel codice. In realtà è rimasto bloccato a 'df = df ['all']. Str.extract ('\ t'.join ([' (. *) '] * (N + 1)), expand = True)' riga .. . qualche idea? –

+0

Ho cambiato il codice (quindi non utilizza più quella riga) e ho aggiunto una sezione su come leggere un file TSV in blocchi. Se il chunksize non è troppo grande, dovresti essere in grado di vedere i risultati più rapidamente. Inoltre, forse non hai bisogno di formare un enorme DataFrame; forse è possibile elaborare il TSV in blocchi in modo iterativo. – unutbu

+0

'df = df.set_index ('field', append = True)' genera 'TypeError: tipo di dati" index "non compreso' ... Puoi provare il mio [File di dati (solo 30Mb)] (https: // drive.google.com/open?id=0BxWmwAIo1D_nUUpqTU5TdDlaUHc) ... In realtà non ho idea del perché. Sembra essere un bug di panda –

4

È possibile utilizzare Panda vectorized string operations, in particolare 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 (ultima chiamata ultima) in () 8 # controlla se le righe contengono questo nome di colonna ----> 9 is_valid & = Clean_Data [column] .str.contiene (colonna) 11 righe # goccia in cui non tutti gli elementi contenenti i rispettivi nomi delle colonne ValueError: operandi non potevano essere trasmessi insieme con le forme (10,) (75618,) (10,) ' –

+0

Spiacente, c'è stato un refuso nell'inizializzazione di 'is_valid'. Prova ora. –

3

Non riesco ad aggiungere commenti, quindi lo posterò come risposta (in realtà si tratta di un commento in risposta al tuo commento sull'utilizzo della memoria e sul runtime).

Una cosa che è necessario considerare, per file di grandi dimensioni (100 GB), è che non si intende leggere questi file in memoria. È possibile impostare la dimensione del blocco per i panda “Large data” work flows using pandas o How to read a 6 GB csv file with pandas oppure utilizzare il generatore di rendimento con il modulo csv e leggere i file riga/riga per riga. Reading a huge .csv in python

Incorporando commento di @ unutbu sull'utilizzo regex per ordinare le voci in colonne, dato che i fieldnames sono così chiaramente delimitate per ogni cella (cioè r'(.*)=(.*)' è tutto ciò che è richiesto - anche se ci può essere qualche correzione degli errori necessario) dovrebbe essere tutto ciò di cui hai bisogno (anche, come si dice, l'eliminazione di intere righe a causa di alcuni dati mancanti non è un approccio tipico o consigliato).