Comment ignorer la première ligne de données lors du traitement des données CSV?

113

Je demande à Python d'imprimer le nombre minimum d'une colonne de données CSV, mais la ligne du haut est le numéro de la colonne, et je ne veux pas que Python prenne en compte la ligne du haut. Comment puis-je m'assurer que Python ignore la première ligne?

Voici le code pour l'instant:

import csv

with open('all16.csv', 'rb') as inf:
    incsv = csv.reader(inf)
    column = 1                
    datatype = float          
    data = (datatype(column) for row in incsv)   
    least_value = min(data)

print least_value

Pourriez-vous également expliquer ce que vous faites, pas seulement donner le code? Je suis très très nouveau sur Python et je voudrais m'assurer de tout comprendre.

Martineau
la source
5
Êtes-vous conscient que vous créez simplement un générateur qui renvoie un 1.0pour chaque ligne de votre fichier et que vous prenez ensuite le minimum, ce qui va être 1.0?
Wooble le
@Wooble Techniquement, c'est un gros générateur de 1.0. :)
Dougal
@Wooble good catch - ... datatype(row[column]... c'est ce que je suppose que l'OP essaie de réaliser
Jon Clements
J'ai demandé à quelqu'un d'écrire ce code pour moi et je n'ai pas compris cela, alors merci haha!

Réponses:

106

Vous pouvez utiliser une instance de la classe csvdu module Snifferpour déduire le format d'un fichier CSV et détecter si une ligne d'en-tête est présente avec la next()fonction intégrée pour sauter la première ligne uniquement lorsque cela est nécessaire:

import csv

with open('all16.csv', 'r', newline='') as file:
    has_header = csv.Sniffer().has_header(file.read(1024))
    file.seek(0)  # Rewind.
    reader = csv.reader(file)
    if has_header:
        next(reader)  # Skip header row.
    column = 1
    datatype = float
    data = (datatype(row[column]) for row in reader)
    least_value = min(data)

print(least_value)

Puisque datatypeet columnsont codés en dur dans votre exemple, il serait légèrement plus rapide de traiter rowcomme ceci:

    data = (float(row[1]) for row in reader)

Remarque: le code ci-dessus est pour Python 3.x. Pour Python 2.x, utilisez la ligne suivante pour ouvrir le fichier au lieu de ce qui est affiché:

with open('all16.csv', 'rb') as file:
Martineau
la source
2
Au lieu de has_header(file.read(1024)), cela a-t-il un sens d'écrire has_header(file.readline())? Je vois beaucoup cela, mais je ne comprends pas comment has_reader()pourrait détecter s'il y a ou non un en-tête à partir d'une seule ligne du fichier CSV ...
Anto
1
@Anto: Le code de ma réponse est basé sur "l'exemple pour l'utilisation de Sniffer" dans la documentation , donc je suppose que c'est la manière prescrite de le faire. Je conviens que le faire sur la base d'une seule ligne de données ne semble pas que ce serait toujours assez de données pour faire une telle détermination - mais je n'ai aucune idée puisque la façon dont les Sniffertravaux ne sont pas décrits. FWIW Je n'ai jamais vu has_header(file.readline())être utilisé et même si cela fonctionnait la plupart du temps, je serais très méfiant de l'approche pour les raisons énoncées.
martineau
Merci pour votre contribution. Néanmoins , il semble que l' utilisation file.read(1024) génère des erreurs dans csv python lib :. Voir aussi ici par exemple.
Anto
@Anto: Je n'ai jamais rencontré une telle erreur - 1024 octets, ce n'est pas beaucoup de mémoire après tout - et cela n'a pas non plus posé de problème pour beaucoup d'autres personnes en fonction des votes positifs que cette réponse a reçus (ainsi que des milliers de des personnes ayant lu et suivi la documentation). Pour ces raisons, je soupçonne fortement que quelque chose d'autre est à l'origine de votre problème.
martineau
J'ai rencontré exactement la même erreur dès que je suis passé de readline()à read(1024). Jusqu'à présent, je n'ai réussi à trouver que des personnes qui sont passées à readline pour résoudre le problème de csv.dialect.
Anto
75

Pour sauter la première ligne, appelez simplement:

next(inf)

Les fichiers en Python sont des itérateurs sur des lignes.

jfs
la source
22

Dans un cas d'utilisation similaire, j'ai dû sauter des lignes ennuyeuses avant la ligne avec mes noms de colonnes réels. Cette solution a bien fonctionné. Lisez d'abord le fichier, puis passez la liste à csv.DictReader.

with open('all16.csv') as tmp:
    # Skip first line (if any)
    next(tmp, None)

    # {line_num: row}
    data = dict(enumerate(csv.DictReader(tmp)))
Maarten
la source
Merci Veedrac. Heureux d'apprendre ici, pouvez-vous suggérer des modifications qui résoudraient les problèmes que vous citez? Ma solution fait le travail, mais il semble qu'elle pourrait être encore améliorée?
Maarten
1
Je vous ai donné une modification qui remplace le code par quelque chose qui devrait être identique (non testé). N'hésitez pas à revenir en arrière si cela ne correspond pas à ce que vous voulez dire. Je ne sais toujours pas pourquoi vous créez le datadictionnaire, et cette réponse n'ajoute pas vraiment quoi que ce soit par rapport à celle acceptée.
Veedrac
Merci Veedrac! Cela semble vraiment très efficace. J'ai posté ma réponse parce que celle acceptée ne fonctionnait pas pour moi (je ne me souviens plus de la raison). Quel serait le problème de définir data = dict () et de le remplir immédiatement (par rapport à votre suggestion)?
Maarten
1
Ce n'est pas mal de le faire data = dict()et de le remplir, mais c'est inefficace et pas idiomatique. De plus, il faut utiliser dict literals ( {}) et enumeratemême alors.
Veedrac
1
FWIW, vous devriez répondre à mes messages avec @Veedracsi vous voulez être sûr d'être averti, bien que Stack Overflow semble être capable de deviner à partir du nom d'utilisateur. (Je n'écris pas @Maartencar le répondeur sera averti par défaut.)
Veedrac
21

Emprunté au livre de recettes python ,
un code de modèle plus concis pourrait ressembler à ceci:

import csv
with open('stocks.csv') as f:
    f_csv = csv.reader(f) 
    headers = next(f_csv) 
    for row in f_csv:
        # Process row ...
tibia
la source
19

Vous utiliseriez normalement next(incsv)ce qui avance l'itérateur d'une ligne, vous ignorez donc l'en-tête. L'autre (disons que vous vouliez sauter 30 lignes) serait:

from itertools import islice
for row in islice(incsv, 30, None):
    # process
Jon Clements
la source
6

utilisez csv.DictReader au lieu de csv.Reader. Si le paramètre fieldnames est omis, les valeurs de la première ligne du fichier csv seront utilisées comme noms de champ. vous pourrez alors accéder aux valeurs de champ en utilisant la ligne ["1"] etc

iruvar
la source
2

Le nouveau paquet «pandas» pourrait être plus pertinent que «csv». Le code ci-dessous lira un fichier CSV, interprétant par défaut la première ligne comme en-tête de colonne et trouvera le minimum entre les colonnes.

import pandas as pd

data = pd.read_csv('all16.csv')
data.min()
Finn Årup Nielsen
la source
et vous pouvez aussi l'écrire sur une ligne:pd.read_csv('all16.csv').min()
Finn Årup Nielsen
1

Eh bien, ma mini bibliothèque de wrapper ferait également l'affaire.

>>> import pyexcel as pe
>>> data = pe.load('all16.csv', name_columns_by_row=0)
>>> min(data.column[1])

En attendant, si vous savez quel est l'index de colonne d'en-tête un, par exemple "Colonne 1", vous pouvez le faire à la place:

>>> min(data.column["Column 1"])
chfw
la source
1

Pour moi, le moyen le plus simple est d'utiliser la gamme.

import csv

with open('files/filename.csv') as I:
    reader = csv.reader(I)
    fulllist = list(reader)

# Starting with data skipping header
for item in range(1, len(fulllist)): 
    # Print each row using "item" as the index value
    print (fulllist[item])  
Clint Hart
la source
1

Parce que cela est lié à quelque chose que je faisais, je vais partager ici.

Que faire si nous ne savons pas s'il y a un en-tête et que vous n'avez pas non plus envie d'importer du sniffer et d'autres choses?

Si votre tâche est basique, comme l'impression ou l'ajout à une liste ou un tableau, vous pouvez simplement utiliser une instruction if:

# Let's say there's 4 columns
with open('file.csv') as csvfile:
     csvreader = csv.reader(csvfile)
# read first line
     first_line = next(csvreader)
# My headers were just text. You can use any suitable conditional here
     if len(first_line) == 4:
          array.append(first_line)
# Now we'll just iterate over everything else as usual:
     for row in csvreader:
          array.append(row)
Roy W.
la source
1

La documentation du module CSV Python 3 fournit cet exemple:

with open('example.csv', newline='') as csvfile:
    dialect = csv.Sniffer().sniff(csvfile.read(1024))
    csvfile.seek(0)
    reader = csv.reader(csvfile, dialect)
    # ... process CSV file contents here ...

Le Snifferessaiera de détecter automatiquement de nombreuses choses sur le fichier CSV. Vous devez appeler explicitement sa has_header()méthode pour déterminer si le fichier a une ligne d'en-tête. Si tel est le cas, ignorez la première ligne lors de l'itération des lignes CSV. Vous pouvez le faire comme ceci:

if sniffer.has_header():
    for header_row in reader:
        break
for data_row in reader:
    # do something with the row
Lassi
la source
0

J'utiliserais tail pour me débarrasser de la première ligne indésirable:

tail -n +2 $INFIL | whatever_script.py 
Karel Adams
la source
0

ajoutez simplement [1:]

exemple ci-dessous:

data = pd.read_csv("/Users/xyz/Desktop/xyxData/xyz.csv", sep=',', header=None)**[1:]**

cela fonctionne pour moi dans iPython

l'esprit curieux
la source
0

Python 3.X

Poignées UTF8 BOM + HEADER

C'était assez frustrant que le csvmodule ne puisse pas facilement obtenir l'en-tête, il y a aussi un bogue avec la nomenclature UTF-8 (premier caractère du fichier). Cela fonctionne pour moi en utilisant uniquement le csvmodule:

import csv

def read_csv(self, csv_path, delimiter):
    with open(csv_path, newline='', encoding='utf-8') as f:
        # https://bugs.python.org/issue7185
        # Remove UTF8 BOM.
        txt = f.read()[1:]

    # Remove header line.
    header = txt.splitlines()[:1]
    lines = txt.splitlines()[1:]

    # Convert to list.
    csv_rows = list(csv.reader(lines, delimiter=delimiter))

    for row in csv_rows:
        value = row[INDEX_HERE]
Christophe Roussy
la source
0

Je convertirais csvreader en liste, puis ferais apparaître le premier élément

import csv        

with open(fileName, 'r') as csvfile:
        csvreader = csv.reader(csvfile)
        data = list(csvreader)               # Convert to list
        data.pop(0)                          # Removes the first row

        for row in data:
            print(row)
Tim John
la source
0

Python 2.x

csvreader.next()

Renvoie la ligne suivante de l'objet itérable du lecteur sous forme de liste, analysée en fonction du dialecte actuel.

csv_data = csv.reader(open('sample.csv'))
csv_data.next() # skip first row
for row in csv_data:
    print(row) # should print second row

Python 3.x

csvreader.__next__()

Renvoie la ligne suivante de l'objet itérable du lecteur sous la forme d'une liste (si l'objet a été renvoyé par reader ()) ou d'un dict (s'il s'agit d'une instance de DictReader), analysée en fonction du dialecte actuel. Habituellement, vous devriez appeler cela comme suivant (lecteur).

csv_data = csv.reader(open('sample.csv'))
csv_data.__next__() # skip first row
for row in csv_data:
    print(row) # should print second row
Patel Romil
la source