2017-12-27 2 views
-1

Kann jemand bitte die groupby-Operation erklären und die Lambda-Funktion auf this SO post?Lambda-Funktion mit itertools count() und groupby()

key=lambda k, line=count(): next(line) // chunk

import tempfile 
from itertools import groupby, count 

temp_dir = tempfile.mkdtemp() 

def tempfile_split(filename, temp_dir, chunk=4000000): 
    with open(filename, 'r') as datafile: 

    # The itertools.groupby() function takes a sequence and a key function, 
    # and returns an iterator that generates pairs. 

    # Each pair contains the result of key_function(each item) and 
    # another iterator containing all the items that shared that key result. 

     groups = groupby(datafile, key=lambda k, line=count(): next(line) // chunk) 
     for k, group in groups: 

      print(key, list(group)) 

      output_name = os.path.normpath(os.path.join(temp_dir + os.sep, "tempfile_%s.tmp" % k)) 
      for line in group: 
       with open(output_name, 'a') as outfile: 
        outfile.write(line) 

Edit: Es dauerte eine Weile, meinen Kopf um die Lambda-Funktion mit groupby verwendet zu wickeln. Ich glaube nicht, dass ich beide sehr gut verstanden habe.

Martijn erklärte es wirklich gut, aber ich habe eine Follow-up-Frage. Warum wird line=count() jedes Mal als Argument an die Lambda-Funktion übergeben? Ich habe versucht, die Variable line zu count() nur einmal außerhalb der Funktion zuweisen.

line = count() 
    groups = groupby(datafile, key=lambda k, line: next(line) // chunk) 

und es ergab TypeError: <lambda>() missing 1 required positional argument: 'line'

Auch next auf count() direkt innerhalb der Lambda-Ausdruck nennen, führte in allen Zeilen in der Eingabedatei gebündelten zusammenkommen, dh ein einzelner Schlüssel durch die groupby Funktion erzeugt wurde .

groups = groupby(datafile, key=lambda k: next(count()) // chunk) 

Ich lerne Python auf eigene Faust, so dass jede Hilfe oder Hinweise auf Referenzmaterialien/PyCon Gespräche sehr geschätzt werden. Alles wirklich!

Antwort

3

itertools.count() ist ein unendlicher Iterator mit steigenden ganzen Zahlen.

Die lambda speichert eine Instanz als Schlüsselwortargument. Jedes Mal, wenn das Lambda aufgerufen wird, verweist die lokale Variable line auf dieses Objekt. next() vorrückt einen Iterator, der nächste Wert Abrufen:

>>> from itertools import count 
>>> line = count() 
>>> next(line) 
0 
>>> next(line) 
1 
>>> next(line) 
2 
>>> next(line) 
3 

So next(line) die nächste Zählung in der Sequenz abruft, und teilt diesen Wert durch chunk (nur der ganzzahlige Teil der Division nehmen). Das k Argument wird ignoriert.

Da eine ganzzahlige Division verwendet wird, wird das Ergebnis der lambdachunk Wiederholungen einer steigenden Ganzzahl sein; wenn chunk 3 ist, dann bekommt man 0 dreimal, dann 1 dreimal, dann 2 dreimal, etc:

>>> chunk = 3 
>>> l = lambda k, line=count(): next(line) // chunk 
>>> [l('ignored') for _ in range(10)] 
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3] 
>>> chunk = 4 
>>> l = lambda k, line=count(): next(line) // chunk 
>>> [l('ignored') for _ in range(10)] 
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2] 

Es ist dieser resultierende Wert, dass groupby() Gruppen der datafile iterable durch, die Herstellung von Gruppen von chunk Linien.

wenn sie über die Ergebnisse mit groupby()for k, group in groups: Looping, k ist die Zahl, die die lambda hergestellt und die Ergebnisse sind gruppiert nach; Die for Schleife im Code ignoriert dies. group ist eine iterierbare Zeile von datafile und enthält immer chunk Zeilen.

+0

Vielen Dank. Schätze deine Hilfe! – theguyoverthere

1

Als Reaktion auf die aktualisierte OP ...

Der Iterator itertools.groupby bietet Möglichkeiten zum Gruppieren von Elementen und gibt mehr Kontrolle, wenn eine Schlüsselfunktion definiert ist. Sehen Sie mehr auf how itertools.groupby() works.

Die Funktion lambda ist eine funktionale Kurzschreibweise für eine normale Funktion. Zum Beispiel:

>>> keyfunc = lambda k, line=count(): next(line) 

entsprechen diese reguläre Funktion:

>>> def keyfunc(k, line=count()): 
...  return next(line) // chunk 

Schlüsselwörter: Iterator, funktionale Programmierung, anonyme Funktionen


Einzelheiten

Warum wird line=count() jedes Mal als Argument an die Lambda-Funktion übergeben?

Der Grund ist der gleiche für normale Funktionen. Der Parameter line selbst ist ein Positionsargument. Wenn ein Wert zugewiesen wird, wird er zum Standard Schlüsselwortargument. Sehen Sie mehr auf positional vs. keyword arguments.

Sie noch line=count() außerhalb der Funktion durch die Zuweisung des Ergebnisses zu einem Schlüsselwort-Argument definieren:

>>> chunk = 3 
>>> line=count() 
>>> keyfunc = lambda k, line=line: next(line) // chunk  # make `line` a keyword arg 
>>> [keyfunc("") for _ in range(10)] 
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3] 
>>> [keyfunc("") for _ in range(10)] 
[3, 3, 4, 4, 4, 5, 5, 5, 6, 6]        # note `count()` continues 

... Aufruf als nächstes auf count() direkt innerhalb der Lambda-Ausdruck, führte in allen Zeilen in der Eingabedatei also zusammen ein einzelner Schlüssel gebündelten immer durch die groupby Funktion erzeugt wurde ...

das folgende Experiment Versuchen mit count():

>>> numbers = count() 
>>> next(numbers) 
0 
>>> next(numbers) 
1 
>>> next(numbers) 
2 

Wie erwartet, werden Sie feststellen, next() wird das nächste Element aus dem count() Iterator ergibt. (Eine ähnliche Funktion wird Iteration eines Iterators mit einer for-Schleife genannt). Was hier einzigartig ist, ist, dass Generatoren nicht zurückgesetzt werden - next() gibt einfach das nächste Element in der Zeile (wie im vorherigen Beispiel).

@Martijn Pieters wies darauf hin next(line) // chunk berechnet eine ganzzahlige Ganzzahl, die von groupby verwendet wird, um jede Zeile zu identifizieren (Bündelung ähnlicher Zeilen mit ähnlichen IDs zusammen), die ebenfalls erwartet wird. Weitere Informationen dazu, wie groupby funktioniert, finden Sie in den Referenzen.

Referenzen

+1

Vielen Dank für die Aufklärung! – theguyoverthere

Verwandte Themen