3 votes

Faire correspondre plusieurs fonctions à des lignes CSV

J'essaie d'effectuer des conversions de type sur des données CSV qui sont entièrement composées de chaînes de caractères. Je pensais utiliser un dictionnaire de noms d'en-têtes de fonctions et mapper ces fonctions sur chaque ligne du CSV. Je suis juste un peu bloqué sur la façon de mapper efficacement plusieurs fonctions sur une ligne. Je pensais énumérer les en-têtes et créer un nouveau dictionnaire d'indices de fonctions :

header_map = {'Foo':str,
              'Bar':str,
              'FooBar':float}

csv_data = [('Foo', 'Bar', 'FooBar'),
            #lots of data...
           ]

index_map = {}

#enumerate the rows and create a dictionary of index:function
for i, header in enumerate(csv_data[0]):
    index_map[i] = header_map[header]

#retrieve the function for each index and call it on the value
new_csv = [[index_map[i](value) for i, value in enumerate(row)] 
           for row in csv_data[1:]]

Je suis simplement curieux de savoir si quelqu'un connaît un moyen plus simple et efficace d'accomplir ce type d'opération ?

1voto

Lev Levitsky Points 25303

Je n'ai pas testé (pas d'entrée d'échantillon), mais cela semble faire ce que vous voulez :

heads = csv_data[0]
new_csv = heads + [
              tuple(header_map[head](item) for head, item in zip(heads, row))
          for row in csv_data[1:]]

1voto

unutbu Points 222216

Voici une méthode, using_converter qui est légèrement plus rapide :

import itertools as IT

header_map = {'Foo':str,
              'Bar':str,
              'FooBar':float}

N = 20000
csv_data = [('Foo', 'Bar', 'FooBar')] + [('Foo', 'Bar', 1123.451)]*N

def original(csv_data):
    index_map = {}
    #enumerate the rows and create a dictionary of index:function
    for i, header in enumerate(csv_data[0]):
        index_map[i] = header_map[header]

    #retrieve the appropriate function for each index and call it on the value
    new_csv = [[index_map[i](value) for i, value in enumerate(row)]
               for row in csv_data[1:]]
    return new_csv

def using_converter(csv_data):
    converters = IT.cycle([header_map[header] for header in csv_data[0]])
    conv = converters.next
    new_csv = [[conv()(item) for item in row] for row in csv_data[1:]]
    return new_csv

def using_header_map(csv_data):
    heads = csv_data[0]
    new_csv = [
        tuple(header_map[head](item) for head, item in zip(heads, row))
        for row in csv_data[1:]]
    return new_csv

# print(original(csv_data))
# print(using_converter(csv_data))
# print(using_header_map(csv_data))

Analyse comparative avec timeit :

Le code original :

% python -mtimeit -s'import test' 'test.original(test.csv_data)'
100 loops, best of 3: 17.3 msec per loop

Une version légèrement plus rapide (utilisant itertools) :

% python -mtimeit -s'import test' 'test.using_converter(test.csv_data)'
100 loops, best of 3: 15.5 msec per loop

La version de Lev Levitsky :

% python -mtimeit -s'import test' 'test.using_header_map(test.csv_data)'
10 loops, best of 3: 36.2 msec per loop

0voto

Alexey Kachayev Points 3491

Si vous connaissez l'ordre des titres dans l'en-tête, vous pouvez utiliser la liste des fonctions au lieu du dict,

>>> header = [str, str, float]
>>> csv = [("aaa", "bbb", "3.14")] * 10
>>> map(lambda line: map(lambda f, arg: f(arg), header, line), csv)
[['aaa', 'bbb', 3.14], ['aaa', 'bbb', 3.14], ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X