2012-12-13 11 views
6

Muszę wyczyścić plik danych wejściowych w python. Z powodu błędu literowego pole danych może mieć łańcuchy zamiast liczb. Chciałbym zidentyfikować wszystkie pola, które są ciągiem znaków i wypełnić je NaN za pomocą pand. Chciałbym również zarejestrować indeks tych pól.czyszczenie dużych danych przy użyciu python

Jednym z najprostszych sposobów jest przechodzenie przez każde pole i sprawdzanie, czy jest to liczba, czy nie, ale to zajmuje dużo czasu, jeśli dane są duże.

Mój plik CSV zawiera dane podobne do poniższej tabeli:

Country Count Sales 
USA   1 65000 
UK   3 4000 
IND   8  g 
SPA   3 9000 
NTH   5 80000 

.... Załóżmy, że mam 60.000 takich wierszy danych.

Idealnie chciałbym wskazać, że wiersz IND ma nieprawidłową wartość w kolumnie SALES. Wszelkie sugestie, jak to zrobić skutecznie?

+5

Pętla na pliku z '60000' rzędach naprawdę nie powinno brać bardzo długo. Wydaje mi się, że czas spędzony z tą metodą byłby mało zauważalny. Czy możesz pokazać, co próbujesz, a także testy porównawcze, pokazujące, że naprawdę jest to znaczące obciążenie dla Twojego komputera? – mgilson

+0

Jeśli zajmuje to więcej czasu, użyj modułu wieloprocesorowego, ale to naprawdę nie powinno zająć więcej niż kilka sekund, oczywiście w zależności od liczby linii, które chcesz edytować. – sean

Odpowiedz

10

Jest na_values argument read_csv:

na_values: lista podobny lub DICT, domyślnie None
            Dodatkowe struny do uznania NA/NaN.Jeśli DICT minęło, specyficzny per-kolumny NA ceni

df = pd.read_csv('city.csv', sep='\s+', na_values=['g']) 

In [2]: df 
Out[2]: 
    Country Count Sales 
0  USA  1 65000 
1  UK  3 4000 
2  IND  8 NaN 
3  SPA  3 9000 
4  NTH  5 80000 

Korzystanie pandas.isnull, można wybrać tylko te wiersze z NaN w kolumnie 'Sales' lub seria 'Country':

In [3]: df[pd.isnull(df['Sales'])] 
Out[3]: 
    Country Count Sales 
2  IND  8 NaN 

In [4]: df[pd.isnull(df['Sales'])]['Country'] 
Out[4]: 
2 IND 
Name: Country 

jeśli to już w DataFrame możesz użyć apply do konwersji tych łańcuchów, które są liczbami na liczby całkowite (przy użyciu str.isdigit):

df = pd.DataFrame({'Count': {0: 1, 1: 3, 2: 8, 3: 3, 4: 5}, 'Country': {0: 'USA', 1: 'UK', 2: 'IND', 3: 'SPA', 4: 'NTH'}, 'Sales': {0: '65000', 1: '4000', 2: 'g', 3: '9000', 4: '80000'}}) 

In [12]: df 
Out[12]: 
    Country Count Sales 
0  USA  1 65000 
1  UK  3 4000 
2  IND  8  g 
3  SPA  3 9000 
4  NTH  5 80000 

In [13]: df['Sales'] = df['Sales'].apply(lambda x: int(x) 
                if str.isdigit(x) 
                else np.nan) 

In [14]: df 
Out[14]: 
    Country Count Sales 
0  USA  1 65000 
1  UK  3 4000 
2  IND  8 NaN 
3  SPA  3 9000 
4  NTH  5 80000 
+0

nice. dlaczego skomentowałeś kod read_csv? Wydaje się działać. –

+0

@Monir odkomentowany i skomentował 'to_dict()'. Chociaż 'apply' jest ważną częścią mojej odpowiedzi (dziwne, że żadne inne odpowiedzi nie używają tego). –

1

Spróbuj przekonwertować ciąg „sprzedaży” do int, jeśli jest dobrze uformowane potem idzie dalej, jeśli to nie będzie podnieść ValueError które złapiemy i zastąpić posiadacza miejsce.

bad_lines = [] 

with open(fname,'rb') as f: 
    header = f.readline() 
    for j,l in enumerate(f): 
     country,count,sales = l.split() 
     try: 
      sales_count = int(sales) 
     except ValueError: 
      sales_count = 'NaN' 
      bad_lines.append(j) 
     # shove in to your data structure 
     print country,count,sales_count 

może trzeba zmodyfikować linię, która dzieli linię (jak Twój przykład skopiowane jako przestrzeni, a nie karty). Zastąp linię drukowania tym, co chcesz zrobić z danymi. Prawdopodobnie musisz również zmienić "NaN" na pandę NaN.

+0

'int (sprzedaż)' powinno dawać to samo, co 'int (sales.strip())' ('int' nie dba o białe znaki) – mgilson

+1

Możliwe też, że możesz podać' country, count, sales = l. split() 'lub ewentualnie' country, count, sales = l.split (None, 2) ' – mgilson

+0

@mgilson awesome, python jest sprytniejszy niż myślałem (ponownie) – tacaswell

5
import os 
import numpy as np 
import pandas as PD 

filename = os.path.expanduser('~/tmp/data.csv') 
df = PD.DataFrame(
     np.genfromtxt(
      filename, delimiter = '\t', names = True, dtype = '|O4,<i4,<f8')) 
print(df) 

daje

Country Count Sales 
0  USA  1 65000 
1  UK  3 4000 
2  IND  8 NaN 
3  SPA  3 9000 
4  NTH  5 80000 

i znaleźć kraj NaN sprzedaży, można obliczyć

print(y['Country'][np.isnan(y['Sales'])]) 

który otrzymuje się pandas.Series:

2 IND 
Name: Country 
+0

Myślę, że to przewidywanie dtype może nie być możliwe/uzasadnione w przypadku większych ramek danych, zwłaszcza jeśli było o wiele więcej kolumn niż w tym przykładzie zabawki ... –

1
filename = open('file.csv') 
filename.readline() 

for line in filename: 
    currentline = line.split(',') 
    try: 
     int(currentline[2][:-1]) 
    except: 
     print currentline[0], currentline[2][:-1] 

IND g

1

proponuję użyć wyrażenia regularnego:

import re 

ss = '''Country Count Sales 
USA ,  3 , 65000 
UK ,  3 , 4000 
IND ,  8 ,  g 
SPA ,  ju , 9000 
NTH ,  5 , 80000 
XSZ , rob , k3''' 

with open('fofo.txt','w') as f: 
    f.write(ss) 

print ss 
print 

delimiter = ',' 

regx = re.compile('(.+?(?:{0}))' 
        '((*\d+?)| *.+?)' 
        '(*(?:{0}))' 
        '((*\d+?)| *.+?)' 
        '(*\r?\n?)$'.format(delimiter)) 

def READ(filepath, regx = regx): 
    with open(filepath,'rb+') as f: 
     yield f.readline() 
     for line in f: 
      if None in regx.match(line).group(3,6): 
       g2,g3,g5,g6 = regx.match(line).group(2,3,5,6) 
       tr = ('%%%ds' % len(g2) % 'NaN' if g3 is None else g3, 
         '%%%ds' % len(g5) % 'NaN' if g6 is None else g6) 
       modified_line = regx.sub(('\g<1>%s\g<4>%s\g<7>' % tr),line) 
       print ('------------------------------------------------\n' 
         '%r with aberration\n' 
         '%r modified line' 
         % (line,modified_line)) 
       yield modified_line 
      else: 
       yield line 

with open('modified.txt','wb') as g: 
    g.writelines(x for x in READ('fofo.txt')) 

wynik

Country Count Sales 
USA ,  3 , 65000 
UK ,  3 , 4000 
IND ,  8 ,  g 
SPA ,  ju , 9000 
NTH ,  5 , 80000 
XSZ , rob , k3 

------------------------------------------------ 
'IND ,  8 ,  g\r\n' with aberration 
'IND ,  8 , NaN\r\n' modified line 
------------------------------------------------ 
'SPA ,  ju , 9000\r\n' with aberration 
'SPA , NaN , 9000\r\n' modified line 
------------------------------------------------ 
'XSZ , rob , k3' with aberration 
'XSZ , NaN , NaN' modified line 
Powiązane problemy