Get started learning Python with DataCamp's free Intro to Python tutorial. Learn Data Science by completing interactive coding challenges and watching videos by expert instructors. Start Now!
This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join 11 million other learners and get started learning Python for data science today!
Good news! You can save 25% off your Datacamp annual subscription with the code LEARNPYTHON23ALE25 - Click here to redeem your discount
Kaart, Filter, Reduce
Map, Filter en Reduce zijn paradigmata van functioneel programmeren. Ze stellen de programmeur (jij) in staat om eenvoudiger en kortere code te schrijven, zonder zich noodzakelijkerwijs druk te hoeven maken over ingewikkelde zaken zoals lussen en vertakkingen.
In wezen stellen deze drie functies je in staat om een functie toe te passen op een aantal iterables, in één keer. map
en filter
zijn ingebouwd in Python (in de __builtins__
module) en behoeven geen import. reduce
daarentegen moet geïmporteerd worden omdat het zich in de functools
module bevindt. Laten we een beter begrip krijgen van hoe ze allemaal werken, te beginnen met map
.
Map
De map()
functie in Python heeft de volgende syntaxis:
map(func, *iterables)
Waarbij func
de functie is waarop elk element in iterables
(hoeveel ze ook zijn) zal worden toegepast. Merk je de asterisk(*
) op bij iterables
? Dit betekent dat er zoveel iterables als mogelijk kunnen zijn, zolang func
dat exacte aantal als benodigde invoerargumenten heeft. Voordat we doorgaan naar een voorbeeld, is het belangrijk dat je het volgende opmerkt:
- In Python 2 retourneert de
map()
functie een lijst. In Python 3 daarentegen, retourneert de functie eenmap object
dat een generator object is. Om het resultaat als lijst te krijgen, kan de ingebouwdelist()
functie worden aangeroepen op het map object. i.e.list(map(func, *iterables))
- Het aantal argumenten voor
func
moet gelijk zijn aan het aantal vermeldeiterables
.
Laten we eens kijken hoe deze regels zich uitspelen met de volgende voorbeelden.
Stel je hebt een lijst (iterable
) van mijn favoriete huisdiernamen, allemaal in kleine letters, en ik moet ze in hoofdletters krijgen. Traditioneel, in normale Python-stijl, zou ik zoiets doen:
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []
for pet in my_pets:
pet_ = pet.upper()
uppered_pets.append(pet_)
print(uppered_pets)
Wat dan zou resulteren in ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
Met map()
functies is het niet alleen eenvoudiger, maar ook veel flexibeler. Ik doe simpelweg dit:
# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
Wat ook hetzelfde resultaat zou geven. Merk op dat met behulp van de gedefinieerde map()
syntaxis hierboven, func
in dit geval str.upper
is en iterables
de my_pets
lijst is -- slechts één iterable. Merk ook op dat we de str.upper
functie niet hebben aangeroepen (door dit te doen: str.upper()
), aangezien de map functie dat voor ons doet op elk element in de my_pets
lijst.
Wat nog belangrijker is om te noteren, is dat de str.upper
functie volgens de definitie slechts één argument vereist en daarom hebben we slechts één iterable doorgegeven. Dus, als de functie die je doorgeeft twee, drie of n argumenten vereist, dan moet je twee, drie of n iterables doorgeven. Laat me dit verduidelijken met een ander voorbeeld.
Stel dat ik een lijst heb van cirkeloppervlakten die ik ergens heb berekend, allemaal tot vijf decimalen. En ik moet elk element in de lijst afronden tot zijn positiedecimalen, wat betekent dat ik het eerste element in de lijst moet afronden tot één decimaal, het tweede element in de lijst tot twee decimalen, het derde element in de lijst tot drie decimalen, enzovoort. Met map()
is dit een eitje. Laten we eens kijken hoe.
Python zegent ons al met de ingesloten functie round()
die twee argumenten neemt -- het getal om af te ronden en het aantal decimalen om het getal op af te ronden. Dus, aangezien de functie twee argumenten vereist, moeten we twee iterables doorgeven.
# Python 3
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1, 7)))
print(result)
Zie je de schoonheid van map()
? Kun je de flexibiliteit voorstellen die dit oproept?
De range(1, 7)
functie fungeert als het tweede argument voor de round
functie (het aantal vereiste decimalen per iteratie). Dus als map
door circle_areas
iterereert, wordt tijdens de eerste iteratie, het eerste element van circle_areas
, 3.56773
samen met het eerste element van range(1,7)
, 1
doorgegeven aan round
, waardoor het effectief wordt round(3.56773, 1)
. Tijdens de tweede iteratie, wordt het tweede element van circle_areas
, 5.57668
samen met het tweede element van range(1,7)
, 2
doorgegeven aan round
waardoor het wordt vertaald naar round(5.57668, 2)
. Dit gebeurt totdat het einde van de circle_areas
lijst is bereikt.
Ik weet zeker dat je je afvraagt: "Wat als ik een iterable doorgeef die korter of langer is dan de lengte van de eerste iterable? Dat wil zeggen, wat als ik range(1, 3)
of range(1, 9999)
doorgeef als de tweede iterable in de bovenstaande functie". En het antwoord is simpel: niets! Oké, dat is niet helemaal waar. "Niets" gebeurt in de zin dat de map()
functie geen uitzondering zal opleveren, het zal eenvoudigweg de elementen itereren totdat het geen tweede argument meer kan vinden voor de functie, op dat moment stopt het gewoon en retourneert het resultaat.
Als je bijvoorbeeld result = list(map(round, circle_areas, range(1, 3)))
evalueert, krijg je geen fout zelfs als de lengte van circle_areas
en de lengte van range(1, 3)
verschillen. In plaats daarvan doet Python dit: het neemt het eerste element van circle_areas
en het eerste element van range(1,3)
en geeft het door aan round
. round
evalueert het en slaat het resultaat op. Vervolgens gaat het verder met de tweede iteratie, het tweede element van circle_areas
en het tweede element van range(1,3)
, round
slaat het weer op. Nu, in de derde iteratie (circle_areas
heeft een derde element), neemt Python het derde element van circle_areas
en probeert dan het derde element van range(1,3)
te nemen, maar aangezien range(1,3)
geen derde element heeft, stopt Python eenvoudig en retourneert het resultaat, dat in dit geval simpelweg zou zijn [3.6, 5.58]
.
Ga je gang, probeer het.
# Python 3
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1, 3)))
print(result)
Hetzelfde gebeurt als circle_areas
korter is dan de tweede iterable. Python stopt gewoon wanneer het het volgende element in een van de iterables niet kan vinden.
Om onze kennis van de map()
functie te consolideren, gaan we het gebruiken om onze eigen custom zip()
functie te implementeren. De zip()
functie is een functie die een aantal iterables neemt en vervolgens een tuple creëert die elk van de elementen in de iterables bevat. Net als map()
, retourneert het in Python 3 een generator object, dat eenvoudig kan worden omgezet in een lijst door de ingebouwde list
functie erop aan te roepen. Gebruik de onderstaande interpretatiesessie om een goed begrip van zip()
te krijgen voordat we de onze creëren met map()
.
# Python 3
my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1, 2, 3, 4, 5]
results = list(zip(my_strings, my_numbers))
print(results)
Als bonus, kun je raden wat er zou gebeuren in de bovenstaande sessie als my_strings
en my_numbers
niet van dezelfde lengte zijn? Nee? probeer het! Wijzig de lengte van een van hen.
Op naar onze eigen custom zip()
functie!
# Python 3
my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1, 2, 3, 4, 5]
results = list(map(lambda x, y: (x, y), my_strings, my_numbers))
print(results)
Kijk eens aan! We hebben hetzelfde resultaat als zip
.
Mocht je ook opmerken dat ik niet eens een functie hoefde te creëren met de def my_function()
standaard manier? Dat is hoe flexibel map()
, en Python in het algemeen, is! Ik gebruikte gewoon een lambda
functie. Dit is niet om te zeggen dat het gebruik van de standaard functiedefiniërende methode (van def function_name()
) niet toegestaan is, dat is nog steeds het geval. Ik gaf er gewoon de voorkeur aan om minder code te schrijven (wees "Pythonic").
Dat is alles over map. Naar filter()
Filter
Terwijl map()
elk element in de iterable door een functie laat gaan en het resultaat van alle elementen teruggeeft nadat ze door de functie zijn gegaan, vereist filter()
allereerst dat de functie booleaanse waarden retourneert (waar of onwaar) en vervolgens elk element in de iterable door de functie laat gaan, waarbij de elementen die onwaar zijn worden "gefilterd". Het heeft de volgende syntaxis:
filter(func, iterable)
De volgende punten zijn belangrijk met betrekking tot filter()
:
- In tegenstelling tot
map()
is slechts één iterable vereist. - Het
func
argument moet een booleaanse waarde retourneren. Als dat niet het geval is, retourneertfilter
simpelweg deiterable
die aan haar wordt doorgegeven. Aangezien slechts één iterable noodzakelijk is, is het impliciet datfunc
maar één argument mag innemen. filter
zorgt ervoor dat elk element in de iterable doorfunc
gaat en retourneert alleen degene die evalueren tot waar. Het staat namelijk in de naam – een "filter".
Laten we een paar voorbeelden zien.
Hieronder is een lijst (iterable
) van de scores van 10 studenten in een scheikunde-examen. Laten we degenen filteren die zijn geslaagd met scores meer dan 75... met behulp van filter
.
# Python 3
scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]
def is_A_student(score):
return score > 75
over_75 = list(filter(is_A_student, scores))
print(over_75)
Het volgende voorbeeld zal een palindroom detector zijn. Een "palindroom" is een woord, zin, of reeks die hetzelfde leest van achter naar voren als van voren naar achteren. Laten we woorden filteren die palindromen zijn uit een tuple (iterable
) van vermoedelijke palindromen.
# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
Wat zou moeten leiden tot de uitvoer ['madam', 'anutforajaroftuna']
.
Best net hè? Tot slot, reduce()
Reduce
reduce
past een functie van twee argumenten cumulatief toe op de elementen van een iterable, optioneel beginnend met een initieel argument. Het heeft de volgende syntaxis:
reduce(func, iterable[, initial])
Waarbij func
de functie is waarop elk element in de iterable
cumulatief wordt toegepast, en initial
de optionele waarde is die wordt geplaatst voor de elementen van de iterable in de berekening, en dient als een standaard wanneer de iterable leeg is. Het volgende moet worden opgemerkt over reduce()
:
1. func
vereist twee argumenten, waarvan de eerste het eerste element in iterable
is (als initial
niet wordt meegegeven) en het tweede element in iterable
. Als initial
wordt meegegeven, dan wordt het het eerste argument naar func
en het eerste element in iterable
het tweede element.
2. reduce
"reduceert" (ik weet het, vergeef me) iterable
tot een enkele waarde.
Zoals gebruikelijk, laten we een paar voorbeelden bekijken.
Laten we onze eigen versie van Python's ingebouwde sum()
functie maken. De sum()
functie retourneert de som van alle items in de iterable die eraan wordt doorgegeven.
# Python 3
from functools import reduce
numbers = [3, 4, 6, 9, 34, 12]
def custom_sum(first, second):
return first + second
result = reduce(custom_sum, numbers)
print(result)
Het resultaat, zoals je zou verwachten, is 68
.
Dus, wat is er gebeurd?
Zoals altijd draait het allemaal om iteraties: reduce
neemt de eerste en tweede elementen in numbers
en geeft ze in die volgorde door aan custom_sum
. custom_sum
berekent hun som en retourneert deze aan reduce
. reduce
neemt vervolgens dat resultaat en gebruikt het als het eerste argument voor custom_sum
en neemt het volgende element (derde) in numbers
als het tweede element voor custom_sum
. Het doet dit continu (cumulatief) totdat numbers
is uitgeput.
Laten we kijken wat er gebeurt wanneer ik de optionele initial
-waarde gebruik.
# Python 3
from functools import reduce
numbers = [3, 4, 6, 9, 34, 12]
def custom_sum(first, second):
return first + second
result = reduce(custom_sum, numbers, 10)
print(result)
Het resultaat, zoals je zou verwachten, is 78
omdat reduce
aanvankelijk 10
gebruikt als het eerste argument voor custom_sum
.
Dat is alles over de Map, Reduce en Filter van Python. Probeer de onderstaande oefeningen uit om je begrip van elke functie te verifiëren.
Oefening
In deze oefening ga je elk van map
, filter
en reduce
gebruiken om kapotte code te repareren.
from functools import reduce
# Use map to print the square of each numbers rounded
# to three decimal places
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
# Use filter to print only the names that are less than
# or equal to seven letters
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
# Use reduce to print the product of these numbers
my_numbers = [4, 6, 9, 23, 5]
# Fix all three respectively.
map_result = list(map(lambda x: x, my_floats))
filter_result = list(filter(lambda name: name, my_names, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers, 0)
print(map_result)
print(filter_result)
print(reduce_result)
#### Map
from functools import reduce
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
my_numbers = [4, 6, 9, 23, 5]
map_result = list(map(lambda x: round(x ** 2, 3), my_floats))
filter_result = list(filter(lambda name: len(name) <= 7, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers)
print(map_result)
print(filter_result)
print(reduce_result)
test_output_contains("[18.922, 37.088, 10.562, 95.453, 4.666, 78.854, 21.068]")
test_output_contains("['olumide', 'josiah', 'omoseun']")
test_output_contains("24840")
success_msg("Congrats! Nice work.")
This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join over a million other learners and get started learning Python for data science today!