Как найти количество столбцов в файле, разделенном табуляцией

У меня есть файл, разделенный табуляцией, с 1 миллиардом строк (представьте себе 200+ столбцов вместо 3):

abc -0.123  0.6524  0.325
foo -0.9808 0.874   -0.2341 
bar 0.23123 -0.123124   -0.1232

Если количество столбцов неизвестно, как мне найти количество столбцов в файле, разделенном табуляцией?

Я пробовал это:

import io
with io.open('bigfile', 'r') as fin:
    num_columns = len(fin.readline().split('\t'))

И (из @EdChum, Прочитать файл, разделенный табуляцией, где первый столбец является ключом, а остальные - значениями):

import pandas as pd
num_columns = pd.read_csv('bigfile', sep='\s+', nrows=1).shape[1]  

Как еще я могу получить количество столбцов? И какой способ самый эффективный? (Представьте, что я внезапно получаю файл с неизвестным количеством столбцов, например, более 1 миллиона столбцов)


person alvas    schedule 28.04.2015    source источник
comment
Что не так с последним фрагментом (который я создал), он просто читает одну строку и выдает число?   -  person EdChum    schedule 28.04.2015
comment
или вообще что не так с чтением первой строки файла и подсчетом количества столбцов?   -  person Julien Spronck    schedule 28.04.2015
comment
@EdChum, я просто хочу проверить, есть ли другие способы получить количество столбцов, а затем сравнить их.   -  person alvas    schedule 28.04.2015
comment
Хорошо, дайте мне знать, если это самый быстрый, мне было бы интересно узнать, как складываются панды.   -  person EdChum    schedule 28.04.2015
comment
Я пробовал синхронизировать другой код, но pandas выдает ошибку StopIteration:   -  person Padraic Cunningham    schedule 28.04.2015
comment
Раскошелиться на это... awk '{print NF;quit}' file   -  person Mark Setchell    schedule 28.04.2015


Ответы (2)


Некоторые тайминги в файле с 100000 столбцов считаются самыми быстрыми, но отличаются на единицу:

In [14]: %%timeit                    
with open("test.csv" ) as f:
    r = csv.reader(f, delimiter="\t")
    len(next(r))
   ....: 
10 loops, best of 3: 88.7 ms per loop

In [15]: %%timeit                    
with open("test.csv" ) as f:
    next(f).count("\t")
   ....: 
100 loops, best of 3: 11.9 ms per loop
with io.open('test.csv', 'r') as fin:
    num_columns = len(next(fin).split('\t'))
    ....: 
 10 loops, best of 3: 133 ms per loop

Использование str.translate на самом деле кажется самым быстрым, хотя вам снова нужно добавить 1:

In [5]: %%timeit
with open("test.csv" ) as f:
    n = next(f)
    (len(n) - len(n.translate(None, "\t")))
   ...: 
100 loops, best of 3: 9.9 ms per loop

Решение pandas дает мне ошибку:

in pandas.parser.TextReader._read_low_memory (pandas/parser.c:7977)()

StopIteration: 

Использование readline добавляет больше накладных расходов:

In [19]: %%timeit
with open("test.csv" ) as f:
    f.readline().count("\t")
   ....: 
10 loops, best of 3: 28.9 ms per loop
In [30]: %%timeit
with io.open('test.csv', 'r') as fin:
    num_columns = len(fin.readline().split('\t'))
   ....: 
10 loops, best of 3: 136 ms per loop

Различные результаты с использованием python 3.4:

In [7]: %%timeit
with io.open('test.csv', 'r') as fin:
    num_columns = len(next(fin).split('\t'))
   ...: 
10 loops, best of 3: 102 ms per loop

In [8]: %%timeit
with open("test.csv" ) as f:
    f.readline().count("\t")
   ...: 

100 loops, best of 3: 12.7 ms per loop   
In [9]:     
In [9]: %%timeit
with open("test.csv" ) as f:
    next(f).count("\t")
   ...: 
100 loops, best of 3: 11.5 ms per loop    
In [10]: %%timeit
with io.open('test.csv', 'r') as fin:
    num_columns = len(next(fin).split('\t'))
   ....: 
10 loops, best of 3: 89.9 ms per loop    
In [11]: %%timeit
with io.open('test.csv', 'r') as fin:
    num_columns = len(fin.readline().split('\t'))
   ....: 
10 loops, best of 3: 92.4 ms per loop   
In [13]: %%timeit     
with open("test.csv" ) as f:
    r = csv.reader(f, delimiter="\t")
    len(next(r))
   ....: 
10 loops, best of 3: 176 ms per loop
person Padraic Cunningham    schedule 28.04.2015
comment
@alvas, добавил разницу, через несколько минут добавлю еще несколько разных методов - person Padraic Cunningham; 28.04.2015
comment
Каким было ваше решение для панд? - person EdChum; 28.04.2015
comment
Ах, хорошо, что произойдет, если вы явно установите low_memory=False? - person EdChum; 28.04.2015
comment
Хорошо, это закончилось через 50,6 с. - person Padraic Cunningham; 28.04.2015
comment
Ха, это отстой! Какую версию pandas, numpy и python вы используете? - person EdChum; 28.04.2015
comment
Конечно, весь код, кроме csv.reader(), предполагает, что каждый символ табуляции разделяет два столбца и что первая строка представляет собой полную первую строку данных. И мне интересно, как выглядит код, где эта самая операция является узким местом. ;-) - person BlackJack; 28.04.2015
comment
@EdChum, 0.16.0 и 1.9.2 соответственно, я полагаю, что pandas обрабатывает все данные, где очень мало делается вызовов next для файлового объекта или средства чтения и один проход по первой строке/строке. - person Padraic Cunningham; 28.04.2015

Существует метод str.count():

h = file.open('path', 'r')
columns = h.readline().count('\t') + 1
h.close()
person mike.k    schedule 28.04.2015