Partage
  • Partager sur Facebook
  • Partager sur Twitter

[python] Extraire des valeurs depuis fichier csv

Sujet résolu
3 novembre 2015 à 14:30:37

Bonjour,

C'est surement très simple et pourtant je n'y parviens pas (il faut dire que je débute).

Je cherche à extraire des valeurs depuis un fichier csv, voici un morceau du fichier

Classe,BDR_BO,BDR_BIBE,SP_BO,SP_BIBE
1,22.65,31.5,38,5
2,31.5,15.75,10,5
3,36.75,15.75,20,5

J'ai un paramètre dans mon code: BDRClasse1 a qui je souhaiterais attribué la valeur "22.65" (ligne 1 et colonne 1)

Comment faire en lisant depuis le csv ?

Merci pour votre aide,

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 15:17:48

Tu as énormément d'outils à ta disposition pour extraire ce genre de donnée:

  • Tu peux créer toi même une fonction qui va lire un fichier au format csv. Dans ce cas renseigne toi sur la méthode split de l'objet str.
  • Tu peux utiliser un module spécifique intégré par défaut à Python: le module csv.
  • Enfin si tu as une distribution où Numpy est installé ou alors si tu as fait toi même ton installation et que tu as installé Numpy tu peux aller voir du côté de la fonction loadtext fournie par le module Numpy. Elle est très performante et facile à comprendre :)

Si tu as un problème de compréhension des outils n'hésites pas à poster une réponse pour demander ce que tu ne comprends pas :)

EDIT: Si tu ne cherches à récupérer que le second élément de la seconde ligne alors utiliser les outils que je t'ai linké plus haut est inutile, tu prendrais un bazooka pour casser un œuf ;) Tu peux tout simplement dire à Python:

  • D'abord tu lis une ligne du fichier. Cette ligne je m'en fiche, elle ne m'intéresse pas.
  • Ensuite tu lis la seconde ligne, et tu récupère le second élément. Ici tu peux aussi vois du côté de split.

-
Edité par Anonyme 3 novembre 2015 à 15:20:56

  • Partager sur Facebook
  • Partager sur Twitter
3 novembre 2015 à 15:20:50

Salut,

Merci pour ta réponse,

Je cherchais effectivement à le faire via le module csv mais je n'y parviens pas,

Je sais renvoyer l’ensemble de la colonne avec "row" mais comment ne renvoyer que la colonne 1/ligne 1?

Actuellement je renvoie tout la colonne:

file=open("test.csv","r")
test=csv.reader(file)
for row in test:
	print(row[1])



-
Edité par ThomC 3 novembre 2015 à 15:23:50

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 15:23:40

Si ton problème est seulement de récupérer que le second élément de la seconde ligne alors inutile d'utiliser le module csv. Voici une fonction qui lit le premier élément de la première ligne, à toi de l'adapter à ton problème :)

def firstElmtFirstLine(file):
    with open(file, 'r') as f:
        ligne = f.readline()
        premierElement = ligne.split(',')[0]
        return premierElement


EDIT:

ThomC a écrit:

Salut,

Merci pour ta réponse,

Je cherchais effectivement à le faire via le module csv mais je n'y parviens pas,

Je sais renvoyer l’ensemble de la colonne avec "row" mais comment ne renvoyer que la colonne 1/ligne 1?

Actuellement je renvoie tout la colonne:

file=open("test.csv","r")
test=csv.reader(file)
for row in test:
	print(row[1])



-
Edité par ThomC il y a 4 minutes


Tu as pensé à tester ça?
test[1][1]
;p

-
Edité par Anonyme 3 novembre 2015 à 15:29:44

  • Partager sur Facebook
  • Partager sur Twitter
3 novembre 2015 à 15:28:23

En fait je cherchez a savoir si avec le module csv on pouvait obtenir d'une manière simple la valeur de n'importe quelle cellule d'un fichier.

Je sais que c'est possible avec le module OpenPyXL:

c = ws.cell('B2')

Le souci c'est que ce module ne fonction pas sous la v3 de python... (sauf si je me trompe o_O)

Edit: j'ai plein de cellules différentes à récupérer

-
Edité par ThomC 3 novembre 2015 à 15:30:04

  • Partager sur Facebook
  • Partager sur Twitter
3 novembre 2015 à 15:34:07


Tu as pensé à tester ça?

test[1][1]


Ça ne marche pas...

>>> file=open("fichiertest.csv","r")
>>> test=csv.reader(file)
>>> test[1][1]
Traceback (most recent call last):
  File "<pyshell#146>", line 1, in <module>
    test[1][1]
TypeError: '_csv.reader' object is not subscriptable



  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 15:39:48

Je viens de regarder dans la doc et... J'avoue que c'est pas hyper pratique comme module! Enfin il est fait pour lire les (très) gros fichiers, mais il est vraiment pas pratique pour les fichiers de petite taille qui peuvent être stockés en mémoire vive...

Du coup tu peux toujours faire une fonction qui te sort l'élément (i,j):

def getElem(file, i, j):
    with open(file, 'r') as f:
        reader = csv.reader(f)
        for line in reader:
            if reader.line_num - 1 == i:
                return line[j]

Mais bon si ton fichier csv est gros la fonction va être très gourmande en ressources pour pas grand chose :(

  • Partager sur Facebook
  • Partager sur Twitter
3 novembre 2015 à 15:48:24

Ta fonction marche super bien, :-°

Si le fichier est de gros volume, que recommande tu alors?

(C'est juste pour info, dans mon cas c'est ok puisque je me balade dans un tableur de 20*15)

Merci pour ton aide,

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 15:51:45

ThomC a écrit:

Ta fonction marche super bien, :-°

Si le fichier est de gros volume, que recommande tu alors?

(C'est juste pour info, dans mon cas c'est ok puisque je me balade dans un tableur de 20*15)

Merci pour ton aide,


Ton problème est réglé, je te réponds plus tard pour l'ouverture sur un gros fichier :)

EDIT:

La méthode la plus rapide que j'ai trouvé pour accéder à un élément spécifique d'un fichier csv très gros (RAM insuffisante pour stocker tout le fichier) a besoin d'une étape d'initialisation:

  • En premier lieu on lit le fichier ligne par ligne et on stocke la position du premier caractère de chaque ligne dans une liste l (grâce à file.readline() pour avancer de ligne et f.tell() pour connaître la position).
  • Ensuite quand on veut lire l'élément à la position (i,j) on va se positionner au début de la j-ième ligne comme ça
    file.seek(l[j])
    puis on va lire la ligne en entier
    ligne = file.readline()
    pour enfin utiliser split et récupérer le i-ème élément:
    element = ligne.split(',')[i]
    Si tu as besoin de changer le type de l'élément tu peux en remplaçant nouveauTypepar le type voulu.
    return nouveauType(element)

Enfin si même les lignes sont très longues pour être lues complètement on peut facilement créer une fonction genSplit(caracter) qui va retourner un générateur au lieu d'une liste, avec le même comportement que split ou même un fonction iSplit(caracter, n) qui va retourner l'élément avant l’occurrence numéro n de caracter.

-
Edité par Anonyme 3 novembre 2015 à 16:23:53

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 18:00:29

Juste une petite remarque, je pense que

for line in f:
    # suite

est un tout petit peu plus courant que l'utilisation de

line = f.readline()




  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 20:59:02

oldProgrammer a écrit:

Juste une petite remarque, je pense que

for line in f:
    # suite

est un tout petit peu plus courant que l'utilisation de

line = f.readline()




Je sais mais je pense que le readline() est plus adapté ici.. En fait on n'a besoin de lire qu'une seule ligne, et la position de cette ligne n'est pas fixe donc utiliser la structure en for comme tu le propose est peut être plus courant mais surtout (il me semble) moins bon du côté sémantique, et même peut-être du côté des performances (condition pour quitter le for + mécanisme d'exception derrière la fin de boucle etc.).. 

En plus si la ligne est trop conséquente pour être stockée en mémoire RAM on peut rapidement changer readline() en read(tampon) alors qu'avec le for c'est tout simplement pas faisable ;) 

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 22:03:14

et même peut-être du côté des performances

Ça sûr que non, j'aurais tendance à dire le contraire...

Mais je n'ai pas vu dans son post qu'il cherchait à ne récupérer que la 1ère ligne ! Dans son 1er post je n'avais pas cette impression en tout cas.

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
3 novembre 2015 à 23:17:29

oldProgrammer a écrit:

et même peut-être du côté des performances

Ça sûr que non, j'aurais tendance à dire le contraire...

Si tu as besoin de lire le fichier en entier alors oui effectivement c'est moins performant (encore que... c'est pas significatif). Voici mes tests:

In [1]: def createFile(n):
    with open("test.txt", 'w') as f:
        for i in range(n):
            for j in range(n):
                f.write( chr((i+j)%91 + 31) if (i+j)%91!=0 else ',' )
            f.write('\n')
   ...:             

In [2]: def methodeReadline(file):
    file.seek(0)
    line = file.readline()
    while line:
        line = file.readline()
        A = len(line)
   ...:         

In [3]: def methodeFor(file):
    file.seek(0)
    for line in file:
        # Un peu de code
        A = len(line)
   ...:         

In [4]: createFile(5000)

In [5]: f = open("test.txt", 'r')

In [6]: %timeit methodeFor(f)
10 loops, best of 3: 52.5 ms per loop

In [7]: %timeit methodeReadline(f)
10 loops, best of 3: 53.6 ms per loop

Par contre quand on rentre dans le détails et qu'on colle le plus possible au cas de ThomC on va avoir:

def methodeForThomC_nonOpti(file, i, j):
    file.seek(0)
    countLine, line = 0, ""
    for l in file:
        countLine += 1
        if countLine == j:
            line = l
            break
    # Maintenant on a la bonne ligne, on peut récupérer l'élément j
    # On va récupérer un élément petit parce que le fichier de départ est pas super bien créé
    element = line.split(',')[i%5]

def methodeForThomC_nonOpti2(file, i, j):
    file.seek(0)
    line = ""
    for countLine, l in enumerate(file):
        if countLine == j:
            line = l
            break
    element = line.split(',')[i%5]

def methodeForThomC_opti(file, i, j):
    file.seek(0)
    file.seek(5000*j)
    line = ""
    for l in file:
        line = l
        break
        # On break desuite parce qu'on est déjà sur la bonne ligne
    element = line.split(',')[i%5]

def methodeReadLineThomC(file, i, j):
    file.seek(0)
    file.seek(5000*j)
    line = file.readline()
    element = line.split(',')[i%5]

Les fonctions définies (je pense que tu voulais essayer les méthodes indicées nonOpti, mais j'en suis pas certain). Et voici les résultats:

In [8]: %timeit methodeForThomC_opti(file, 1637, 4500)
10000 loops, best of 3: 56.3 µs per loop

In [9]: %timeit methodeReadLineThomC(file, 1637, 4500)
The slowest run took 4.75 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 55.8 µs per loop

In [10]: %timeit methodeReadLineThomC(file, 1637, 4500)
The slowest run took 4.63 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 55.7 µs per loop

In [11]: %timeit methodeForThomC_nonOpti(file, 1637, 4500)
10 loops, best of 3: 54.1 ms per loop

In [12]: %timeit methodeForThomC_nonOpti2(file, 1637, 4500)
10 loops, best of 3: 54.4 ms per loop

In [13]: %timeit methodeForThomC_opti(file, 1637, 4500)
The slowest run took 4.70 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 54.8 µs per loop

In [14]: %timeit methodeReadLineThomC(file, 1637, 4500)
The slowest run took 4.66 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 56.6 µs per loop

In [15]: %timeit methodeReadLineThomC(file, 1637, 4500)
The slowest run took 4.69 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 55.4 µs per loop

Du coup c'est pareil... Une fois la méthode readline gagne et l'autre fois celle avec le for l'emporte, la différence de performance n'est pas assez visible pour se faire ressentir :)

oldProgrammer a écrit:

Mais je n'ai pas vu dans son post qu'il cherchait à ne récupérer que la 1ère ligne ! Dans son 1er post je n'avais pas cette impression en tout cas.


Mais je ne lis pas que la première ligne... Mon f.seek(n) va déplacer le "curseur de lecture" à la ligne qui l'intéresse, soit ici la ligne n°j. Et ensuite on ne lit que la ligne qui l'intéresse, soit la ligne qui est juste après le curseur, la ligne n°j. Du coup on à pas besoin de lire toutes les lignes avant la n°j (quand je parle de très gros fichiers ça peut être des millions de lignes... Si tu cherches la ligne n°1.500.000 alors ta boucle for va mettre pas mal de temps a l'atteindre :o)

-
Edité par Anonyme 3 novembre 2015 à 23:18:22

  • Partager sur Facebook
  • Partager sur Twitter
4 novembre 2015 à 12:59:29

ThomC a écrit:

Le souci c'est que ce module ne fonction pas sous la v3 de python... (sauf si je me trompe o_O)

Je crois que tu te trompe.

Sinon ma solution avec le module csv :

import csv

def my_update(d, newd):

"""
Les pairs clefs/valeur du dictionnaire newd sont ajouté au dictionnaire d
La valeur est mise dans une liste à un seul élément si la clef n'existait
pas déjà dans d, sinon la valeur est ajouté en fin de liste.
"""
for key, value in newd.items():
    if key in d:
        d[key].append(value)
    else:
        d[key] = [value]
        

with open("input.txt","r") as file:

data = csv.DictReader(file)
content = {}
for row in data:
    my_update(content, row)
    

print(content['BDR_BO'][1])

</pre>
  • Partager sur Facebook
  • Partager sur Twitter
per aspera ad astra – comp.lang.c FAQexplication pointeur
6 janvier 2020 à 9:03:16

Sans intérêt pour ThomC qui a, je l'espère, trouvé la solution depuis le temps, mais pour ceux qui tombent sur ce sujet : une façon simple de faire est d'utiliser pandas (à condition que le module soit installé, bien sûr).

import pandas as pd

ma_ligne = 1
ma_colonne = 1
with open("monfichier.csv") as f:
    reader = pd.read_csv(f)
ma_valeur = reader.iloc[ma_ligne, ma_colonne]

-
Edité par MacBeth 6 janvier 2020 à 9:06:47

  • Partager sur Facebook
  • Partager sur Twitter
29 avril 2020 à 16:42:41 - Message modéré pour le motif suivant : Merci d'utiliser le bouton code du forum pour insérer votre code


29 avril 2020 à 17:10:33

Bonjour,

Déterrage

Citation des règles générales du forum :

Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.

Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre.
En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.

Au lieu de déterrer un sujet il est préférable :

  • soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
  • soit de créer un nouveau sujet décrivant votre propre contexte
  • ne pas répondre à un déterrage et le signaler à la modération

Je ferme ce sujet. En cas de désaccord, me contacter par MP.

  • Partager sur Facebook
  • Partager sur Twitter