Saya memiliki serangkaian file *.csv yang sangat berantakan yang sedang dibaca oleh panda. Contoh csv adalah:

Instrument 35392
"Log File Name : station"
"Setup Date (MMDDYY) : 031114"
"Setup Time (HHMMSS) : 073648"
"Starting Date (MMDDYY) : 031114"
"Starting Time (HHMMSS) : 090000"
"Stopping Date (MMDDYY) : 031115"
"Stopping Time (HHMMSS) : 235959"
"Interval (HHMMSS) : 010000"
"Sensor warmup (HHMMSS) : 000200"
"Circltr warmup (HHMMSS) : 000200" 


"Date","Time","","Temp","","SpCond","","Sal","","IBatt",""
"MMDDYY","HHMMSS","","øC","","mS/cm","","ppt","","Volts",""

"Random message here 031114 073721 to 031114 083200"
03/11/14,09:00:00,"",15.85,"",1.408,"",.74,"",6.2,""
03/11/14,10:00:00,"",15.99,"",1.96,"",1.05,"",6.3,""
03/11/14,11:00:00,"",14.2,"",40.8,"",26.12,"",6.2,""
03/11/14,12:00:01,"",14.2,"",41.7,"",26.77,"",6.2,""
03/11/14,13:00:00,"",14.5,"",41.3,"",26.52,"",6.2,""
03/11/14,14:00:00,"",14.96,"",41,"",26.29,"",6.2,""
"message 3"
"message 4"**

Saya telah menggunakan kode ini untuk mengimpor file *csv, memproses tajuk ganda, mengeluarkan kolom kosong, dan kemudian menghapus baris yang menyinggung dengan data yang buruk:

DF = pd.read_csv(BADFILE,parse_dates={'Datetime_(ascii)': [0,1]}, sep=",", \
             header=[10,11],na_values=['','na', 'nan nan'], \
             skiprows=[10], encoding='cp1252')

DF = DF.dropna(how="all", axis=1)
DF = DF.dropna(thresh=2)
droplist = ['message', 'Random']
DF = DF[~DF['Datetime_(ascii)'].str.contains('|'.join(droplist))]

DF.head()

Datetime_(ascii)    (Temp, øC)  (SpCond, mS/cm) (Sal, ppt)  (IBatt, Volts)
0   03/11/14 09:00:00   15.85   1.408   0.74    6.2
1   03/11/14 10:00:00   15.99   1.960   1.05    6.3
2   03/11/14 11:00:00   14.20   40.800  26.12   6.2
3   03/11/14 12:00:01   14.20   41.700  26.77   6.2
4   03/11/14 13:00:00   14.50   41.300  26.52   6.2

Ini berfungsi dengan baik dan keren sampai saya memiliki file yang memiliki 1 baris baris yang salah setelah header: "Pesan acak di sini 031114 073721 hingga 031114 083200"

Kesalahan yang saya terima adalah:

    *C:\Users\USER\AppData\Local\Continuum\Anaconda3\lib\site-
    packages\pandas\io\parsers.py in _do_date_conversions(self, names, data)
   1554             data, names = _process_date_conversion(
   1555                 data, self._date_conv, self.parse_dates, self.index_col,
    -> 1556                 self.index_names, names, 
    keep_date_col=self.keep_date_col)
   1557 
   1558         return names, data
    C:\Users\USER\AppData\Local\Continuum\Anaconda3\lib\site-
    packages\pandas\io\parsers.py in _process_date_conversion(data_dict, 
    converter, parse_spec, index_col, index_names, columns, keep_date_col)
   2975     if not keep_date_col:
   2976         for c in list(date_cols):
    -> 2977             data_dict.pop(c)
   2978             new_cols.remove(c)
   2979 
   KeyError: ('Time', 'HHMMSS')*

Jika saya menghapus baris itu, kodenya berfungsi dengan baik. Demikian pula, jika saya menghapus baris header= kode berfungsi dengan baik. Namun, saya ingin dapat menyimpan ini karena saya membaca ratusan file ini.

Kesulitan: Saya lebih suka untuk tidak membuka setiap file sebelum panggilan ke pandas.read_csv() karena file ini bisa agak besar - jadi saya tidak ingin membaca dan menyimpan beberapa kali! Juga, saya lebih suka solusi panda/pythonic nyata yang tidak melibatkan pembukaan file terlebih dahulu sebagai buffer stringIO untuk menghapus baris yang menyinggung.

1
name goes here 14 Agustus 2017, 20:29

2 jawaban

Jawaban Terbaik

Inilah satu pendekatan, memanfaatkan fakta bahwa skip_rows menerima fungsi yang dapat dipanggil. Fungsi hanya menerima indeks baris yang dipertimbangkan, yang merupakan batasan bawaan dari parameter tersebut.

Dengan demikian, fungsi yang dapat dipanggil skip_test() pertama-tama memeriksa apakah indeks saat ini berada dalam kumpulan indeks yang diketahui untuk dilewati. Jika tidak, maka itu akan membuka file yang sebenarnya dan memeriksa baris yang sesuai untuk melihat apakah isinya cocok.

Fungsi skip_test() sedikit meretas dalam arti bahwa ia memeriksa file yang sebenarnya, meskipun hanya memeriksa hingga indeks baris saat ini yang dievaluasi. Itu juga mengasumsikan bahwa garis buruk selalu dimulai dengan string yang sama (dalam contoh kasus, "foo"), tetapi itu tampaknya merupakan asumsi yang aman mengingat OP.

# example data
""" foo.csv
uid,a,b,c
0,1,2,3
skip me
1,11,22,33
foo
2,111,222,333 
"""

import pandas as pd

def skip_test(r, fn, fail_on, known):
    if r in known: # we know we always want to skip these
        return True
    # check if row index matches problem line in file
    # for efficiency, quit after we pass row index in file
    f = open(fn, "r")
    data = f.read()
    for i, line in enumerate(data.splitlines()):
        if (i == r) & line.startswith(fail_on):
            return True
        elif i > r:
            break
    return False

fname = "foo.csv"
fail_str = "foo"
known_skip = [2]
pd.read_csv(fname, sep=",", header=0, 
            skiprows=lambda x: skip_test(x, fname, fail_str, known_skip))
# output
   uid    a    b    c
0    0    1    2    3
1    1   11   22   33
2    2  111  222  333

Jika Anda tahu persis di baris mana pesan acak akan muncul ketika pesan itu muncul, maka ini akan jauh lebih cepat, karena Anda bisa saja mengatakannya untuk tidak memeriksa isi file untuk indeks apa pun yang melewati baris yang berpotensi menyinggung.

2
andrew_reece 14 Agustus 2017, 19:41

Setelah beberapa mengutak-atik kemarin, saya menemukan solusi dan kemungkinan masalah apa yang mungkin terjadi.

Saya mencoba jawaban fungsi skip_test() di atas, tetapi saya masih mendapatkan kesalahan dengan ukuran tabel:

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader.read (pandas\_libs\parsers.c:10862)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader._read_low_memory (pandas\_libs\parsers.c:11138)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader._read_rows (pandas\_libs\parsers.c:11884)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.TextReader._tokenize_rows (pandas\_libs\parsers.c:11755)()

pandas\_libs\parsers.pyx in pandas._libs.parsers.raise_parser_error (pandas\_libs\parsers.c:28765)()

ParserError: Error tokenizing data. C error: Expected 1 fields in line 14, saw 11

Jadi setelah bermain-main dengan skiprows= saya menemukan bahwa saya tidak mendapatkan perilaku yang saya inginkan saat menggunakan engine='c'. read_csv() masih menentukan ukuran file dari beberapa baris pertama, dan beberapa baris kolom tunggal itu masih diteruskan. Mungkin saya memiliki beberapa baris kolom tunggal yang lebih buruk di set csv saya yang tidak saya rencanakan.

Sebagai gantinya, saya membuat DataFrame berukuran sewenang-wenang sebagai templat. Saya menarik seluruh file .csv, lalu menggunakan logika untuk menghapus baris NaN.

Misalnya, saya tahu bahwa tabel terbesar yang akan saya temui dengan data saya akan memiliki panjang 10 baris. Jadi panggilan saya ke panda adalah:

DF = pd.read_csv(csv_file, sep=',', \
     parse_dates={'Datetime_(ascii)': [0,1]},\
     na_values=['','na', '999999', '#'], engine='c',\ 
     encoding='cp1252', names = list(range(0,10)))

Saya kemudian menggunakan dua baris ini untuk melepaskan baris dan kolom NaN dari DataFrame:

#drop the null columns created by double deliminators
DF = DF.dropna(how="all", axis=1)
DF = DF.dropna(thresh=2)  # drop if we don't have at least 2 cells with real values
0
name goes here 15 Agustus 2017, 13:15