2016-05-06 14 views
6

So. Mamy dane w postaci bałaganu zapisane w pliku TSV, które muszę zanalizować. Jak to wyglądaPython. Pandy. BigData. Brudny plik TSV. Jak pozbyć się danych?

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/ 

A problem jest to, że niektóre wiersze mają różną kolejność kolumn/niektóre z nich brakujących wartości i muszę pozbyć się, że o wysokiej wydajności (od zbiorów danych pracuję z są do 100 gigabajtów).

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) 

Teraz Pozbyłem brakujących wartości, ale jeden problem nadal! ten sposób dane wygląda: enter image description here

I to jak problem wygląda: enter image description here

Jak widać niektóre kolumny są przesunięte. zrobiłem rozwiązanie bardzo niskiej wydajności

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) 

Więc teraz Dane wygląda dobrze ... przynajmniej mogę z nim pracować! Ale jaka jest ALTERNATYWA o wysokiej wydajności do metody, którą wykonałem powyżej?

Aktualizacja kodu unutbu: error 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 

Pandy wersja: 0.18.0-np110py27_0

Aktualizacja

Wszystko działało ... Dzięki wszystkim!

Odpowiedz

5

Załóżmy, że dane TSV takie jak to:

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 

Kolejność pól może być scambled, i nie może być brakujące wartości. Jednak nie musisz upuszczać wierszy tylko dlatego, że pola nie pojawiają się w określonej kolejności. Możesz użyć nazw pól podanych w samych danych wiersza, aby umieścić wartości we właściwych kolumnach.Na przykład,

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) 

daje

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 

Aby obsłużyć dużego pliku TSV, można przetwarzać wiersze w kawałki, a następnie złączyć przetworzone kawałki w jeden DataFrame na koniec:

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) 

Wyjaśnienie: Given 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 

można łączyć wszystkie wartości w jednej kolumnie

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 

a następnie zastosować .str.extract(r'([^=]*)=(.*)') oddzielić nazwę pola z wartości:

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 

aby ułatwić aby odnieść się do części ramki DataFrame, podajmy nazwy kolumn i poziomów opisowych:

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

Jeśli teraz przesunąć kolumnę field do indeksu:

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

i spadek poziomu 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 
... 

możemy uzyskania DataFrame pożądanej postaci przez przenoszenie poziomu indeksu field do indeksu kolumny:

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

Czy możesz skomentować swój kod, aby ja (i wszyscy) mogliśmy podążać za Tobą całkowicie? Hm wydaje się, że iPython działa bardzo wolno z tym kodem. W rzeczywistości utknął w 'df = df ['all']. Str.extract ('\ t'.join ([' (. *) '] * (N + 1)), expand = True)' wiersz .. . jakieś pomysły? –

+0

Zmieniono kod (więc nie używa już tej linii) i dodano sekcję dotyczącą odczytywania pliku TSV w porcjach. Jeśli porcja nie jest zbyt duża, powinieneś szybciej widzieć wyniki. Co więcej, być może nie musisz tworzyć jednej ogromnej DataFrame; być może możesz przetwarzać TSV w porcjach iteracyjnie. – unutbu

+0

'df = df.set_index ('pole', append = True)' throws 'TypeError: typ danych" index "not understand' ... Możesz wypróbować mój [plik danych (tylko 30MB)] (https: // drive.google.com/open?id=0BxWmwAIo1D_nUUpqTU5TdDlaUHc) ... Właściwie nie mam pojęcia dlaczego. Wydaje się być błędem pandy –

4

Można użyć Pandy vectorized string operations konkretnie 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

'---------------------------------------------- ----------------------------- Traceback wartości ValueError (ostatnie ostatnie połączenie) in () 8 # sprawdź, czy wiersze zawierają tę nazwę kolumny ----> 9 is_valid & = Clean_Data [kolumna] .str.zawiera (kolumna) 11 # upuść wiersze, w których nie wszystkie elementy zawierają odpowiednie nazwy kolumn ValueError: operandy nie mogły być transmitowane razem z kształtami (10,) (75618,) (10,) ' –

+0

Niestety, nie było literówka w inicjalizacji 'is_valid'. Spróbuj teraz. –

3

Nie mogę dodawać komentarzy, więc opublikuję to jako odpowiedź (w rzeczywistości jest to komentarz w odpowiedzi na twój komentarz dotyczący użycia pamięci i czasu wykonywania).

Jedną rzeczą, którą należy wziąć pod uwagę, w przypadku dużych plików (100 GB), jest to, że nie będziesz czytać tych plików w pamięci. Możesz ustawić rozmiar porcji pandy “Large data” work flows using pandas lub How to read a 6 GB csv file with pandas lub użyć generatora plonów z modułem csv i przeczytać wiersz plików/wiersz po linii. Reading a huge .csv in python

Zawierające komentarz @ unutbu na temat korzystania regex aby posortować wpisy na kolumny, biorąc pod uwagę, że fieldnames są więc wyraźnie odgraniczone dla każdej komórki (tj r'(.*)=(.*)' jest wszystko, co jest wymagane - chociaż mogą być pewne korekty błędu potrzebne) powinna być wszystko, czego potrzebujesz (także, jak to się mówi, upuszczanie całych wierszy z powodu brakujących danych nie jest typowym lub zalecanym podejściem).

Powiązane problemy