it-swarm.com.de

Was ist eine pythonische Methode, um die folgende Transformation für eine Liste von Diktaten durchzuführen?

Ich habe eine Liste von Diagrammen wie folgt:

l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]

und ich möchte eine Ausgabe dieser Form erhalten:

>>> [('foo', 'bar'), ([1,2,3,4], [5,6,7,8])]

Kurz vor for-looping und appending sehe ich keine Lösung. Gibt es einen intelligenteren Weg als dies zu tun?

names = []
values = []
for d in l:
    names.append(d['name'])
    values.append(d['values'])
28
oarfish

Generatorausdruck verwenden:

l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
v = [Tuple(k["name"] for k in l), Tuple(k["values"] for k in l)]
print(v)

Ausgabe:

[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
29
eyllanesc

Ich würde ein Listenverständnis verwenden (ähnlich wie bei Eyllanesc), wenn ich diesen Code für den öffentlichen Gebrauch schreiben würde. Aber nur zum Spaß, hier ist ein Einzeiler, der keine fors verwendet.

>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> list(Zip(*map(dict.values, l)))
[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

(Beachten Sie, dass dies nur dann zuverlässig funktioniert, wenn Wörterbücher die Einfügereihenfolge beibehalten. Dies ist nicht in allen Python-Versionen der Fall. CPython 3.6 macht dies als Implementierungsdetail, aber es wird nur das Verhalten ab 3.7 garantiert.)

Schnelle Aufschlüsselung des Prozesses:

  • dict.values ​​gibt ein dict_values-Objekt zurück, bei dem es sich um eine Iteration handelt, die alle Werte des Diktats enthält.
  • map nimmt jedes Wörterbuch in l und ruft dict.values ​​auf, wodurch eine Iteration von dict_values-Objekten zurückgegeben wird.
  • Zip(*thing) ist ein klassisches "Transposition" -Rezept, das iterable-of-iterables verwendet und effektiv diagonal gekippt wird. Z.B. [[a, b], [c, d]] wird zu [[a, c], [b, d]]. Dadurch werden alle Namen in einem Tuple und alle Werte in einem anderen gespeichert.
  • list konvertiert das Zip-Objekt in eine Liste.
24
Kevin

Sie können operator.itemgetter zur Garantie Reihenfolge der Werte verwenden:

from operator import itemgetter

fields = ('name', 'values')
res = list(Zip(*map(itemgetter(*fields), L)))

print(res)

[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

Wenn Sie, vorausgesetzt, Python 3.6 oder höher, keine angemessene Einfügungsreihenfolge für Wörterbücher in Ihrer Eingabeliste garantieren können, müssen Sie die Reihenfolge wie oben explizit definieren.

Leistung

Während eine Liste von "Tuple Comprehensions" funktioniert, wird sie unlesbar und ineffizient, wenn mehrere Felder abgefragt werden:

from operator import itemgetter

n = 10**6
L = [{'name': 'foo', 'values': [1,2,3,4], 'name2': 'Zoo', 'name3': 'xyz',
      'name4': 'def'}, {'name': 'bar', 'values': [5,6,7,8], 'name2': 'bart',
      'name3': 'abc', 'name4': 'ghi'}] * n

%timeit [Tuple(k["name"] for k in L), Tuple(k["values"] for k in L),\
         Tuple(k["name2"] for k in L), Tuple(k["name3"] for k in L),
         Tuple(k["name4"] for k in L)]

%timeit fields = ('name', 'values', 'name2', 'name3' ,'name4');\
        list(Zip(*map(itemgetter(*fields), L)))

1 loop, best of 3: 1.25 s per loop
1 loop, best of 3: 1.04 s per loop
10
jpp

Dies ist möglicherweise nicht genau das, was Sie sich vorgestellt haben, aber für solche Tabellendaten finde ich, dass pandas auf lange Sicht normalerweise die beste Lösung ist:

>>> import pandas as pd
>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> df = pd.DataFrame(l)
  name        values
0  foo  [1, 2, 3, 4]
1  bar  [5, 6, 7, 8]

Normalerweise verwenden Sie den Datenrahmen direkt für alles, was Sie tun müssen, Sie können ihn jedoch auch in eine auf Listen basierende Datenstruktur konvertieren:

>>> df['name'].tolist(), df['values'].tolist()
(['foo', 'bar'], [[1, 2, 3, 4], [5, 6, 7, 8]]) 
5
Kale Kundert

Nicht sicher über die Leistung, aber hier ist ein weiterer Take, der Zip() verwendet und auspackt:

list(Zip(*[Tuple(i.values()) for i in l]))

# [('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

Bearbeiten: Wie @DeepSpace hervorgehoben hat, kann es weiter reduziert werden auf:

list(Zip(*(i.values() for i in l)))

Hier ist eine längere, aber deutlichere Antwort, wenn Sie die Bestellungen selbst definieren möchten:

list(Zip(*(Tuple(map(lambda k: i.get(k), ('name', 'values'))) for i in l)))

# [('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
4
Idlehands

karte hierfür verwenden

names = Tuple(map(lambda d: d['name'], l))
values = Tuple(map(lambda d: d['values'], l))
result = [names, values]
3
user3142459

Hier ist eine rekursive Methode:

def trans(l):
  if l:
    res = trans(l[1:])
    res[0], res[1] = (l[0]['name'],) + res[0], (l[0]['values'],) + res[1]
    return res
  return [(),()]
0
greenBox

Erstens: Ihr Code ist gut lesbar und effizient, was für mich Pythonic klingt. Beachten Sie, dass Sie wahrscheinlich keine Liste von Tupeln möchten. Tupel sind unveränderlich , so dass Sie keinen anderen Namen an names anhängen können.

Mit einem einzigen Dikt

Wenn names eindeutig ist, können Sie Ihre Liste von Diktaten in ein großes Diktat konvertieren:

>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> data = {d['name']:d['values'] for d in l}
>>> data
{'foo': [1, 2, 3, 4], 'bar': [5, 6, 7, 8]}

Sie erhalten die gewünschten Informationen direkt:

>>> data.keys()
dict_keys(['foo', 'bar'])
>>> data.values()
dict_values([[1, 2, 3, 4], [5, 6, 7, 8]])

Wenn Sie wirklich eine Liste von Listen wünschen:

>>> [list(data.keys()), list(data.values())]
[['foo', 'bar'], [[1, 2, 3, 4], [5, 6, 7, 8]]]

Mit Pandas

Wenn Sie mit einer großen Liste von Diktaten arbeiten, sollten Sie pandas berücksichtigen.

Sie könnten eine DataFrame direkt initialisieren:

>>> import pandas as pd
>>> df = pd.DataFrame([{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}])
>>> df
  name        values
0  foo  [1, 2, 3, 4]
1  bar  [5, 6, 7, 8]

Wenn Sie die Namen als iterierbar benötigen, erhalten Sie die entsprechende Spalte:

>>> df['name']
0    foo
1    bar
Name: name, dtype: object

Wenn Sie wirklich eine Liste mit Namen benötigen:

>>> list(df['name'])
['foo', 'bar']

Um die Namen und Werte zusammen zu bekommen:

>>> df.values.T
array([['foo', 'bar'],
       [list([1, 2, 3, 4]), list([5, 6, 7, 8])]], dtype=object)
0
Eric Duminil