Archives Wild Code School
Python

Map, Filter, Reduce

Quête 42 sur 66 · 1 image archivée

Déjà fatigué des boucles ? 😉

hermione raise hand

Eh bien, ne cherche pas plus loin mon ami ! Les fonctions map, filter et reduce sont là pour t'aider à virer ces satanées boucles for de ton code.

Comment vont-elles faire ça ? Voyons voir.

Pour comprendre cette quête tu as besoin de connaître :

Contenu 🤓

À la fin de cette quête, tu seras capable de :

✅ manipuler toutes les valeurs d'une ou de plusieurs listes d'un coup grâce à la fonction map ✅ filtrer une liste d'un coup grâce à la fonction filter ✅ effectuer des calculs qui demandent la liste entière grâce à la fonction reduce

La fonction Map

Traiter une seule liste

Imagine-toi au travail, dans le secteur financier. Pour ta première tâche, tu dois convertir tous les prix en USD en valeurs EUR.

Voici les valeurs à convertir :

python
amazon_usd = [213, 217, 221, 348, 331, 290, 277, 273, 209, 335, 240, 280, 278, 207, 304, 309, 204, 292, 261, 265, 226, 242, 262, 304, 250, 296, 202, 281, 294, 244, 231, 204, 275, 350, 242, 251, 312, 276, 219, 246, 214, 337, 273, 239, 218, 252, 324, 212, 232, 214, 220, 205, 339, 347, 275, 290, 345, 234, 338, 206, 328, 230, 209, 301, 291, 332, 348, 281, 208, 283, 328, 232, 200, 248, 316, 204, 242, 265, 300, 209, 257, 217, 347, 220, 260, 305, 221, 257, 281, 216, 329, 207, 205, 313, 321, 333, 231, 273, 323, 245]

D'abord, écrivons la fonction qui transforme les valeurs USD en EUR. Supposons que le taux de change actuel soit 1 USD = 0,86 EUR

python
def usd_to_eur (money):
  return round(money * 0.86, 2)

Tu viens de finir de coder ta fonction. Tu as toute une liste de variables à faire passer dans ta fonction. Peut-être que tu te souviens de la quête sur les compréhensions de liste (list comprehensions), peut-être pas. Peut-être que tu te souviens seulement de la boucle for de base. Pas de souci, on peut y arriver avec une simple boucle for :

python
amazon_eur_fl = [] 
for m_usd in amazon_usd:
  m_eur = usd_to_eur(m_usd)
  amazon_eur_fl.append(m_eur)


print('This are the EUR values of your stock with for loops.')
print(amazon_eur_fl)

Si tu te souviens bien des compréhensions de liste, tu voudras peut-être les utiliser :

python
amazon_eur_lc = [usd_to_eur(m_usd) for m_usd in amazon_usd] 

print('This are the EUR values of your stock with list comprehensions.')
print(amazon_eur_lc)

Ça donne déjà un code plus court. C'est une amélioration, non ?

Utiliser la fonction map va permettre un code encore plus clair et plus court tout en obtenant le même résultat. Voici sa structure :

output_variable = map(function, input_list1, input_list2, ...)

Ici se trouve la documentation de la fonction map.

Dans notre tâche (convertir les valeurs de USD en EUR), nous avons notre fonction et une liste en entrée. La fonction map est une fonction built-in (fonction native, intégrée directement à Python), donc pas besoin de s'inquiéter d'importer quoi que ce soit.

Dans notre cas, le nom de la fonction est usd_to_eur, la variable d'entrée est amazon_usd et la variable de sortie est amazon_eur_m

python
amazon_eur_m = map(usd_to_eur, amazon_usd)
print(amazon_eur_m)

Maintenant, petit problème : est-ce que ça marche ? Aucune idée pour l'instant, au moins ça n'a pas levé d'erreur, mais on ne peut pas voir ce qu'il y a à l'intérieur : la fonction map renvoie un objet !! Plus précisément, un map iterator (un itérateur, une séquence abstraite de résultats que l'on ne peut parcourir qu'une fois). On a besoin d'un conteneur pour y ranger ces résultats ! Alors, on fait quoi maintenant ?

hidden
Click to reveal|||python|||Hide|||1|||
amazon_eur_ml = list(map(usd_to_eur, amazon_usd))
print(amazon_eur_ml)

Félicitations ! C'était facile, non ?

Traiter plusieurs listes

On a réussi la conversion de devise. Maintenant il nous faut comparer deux actions (stocks) différentes pour étudier leur variabilité. On veut savoir si la différence entre elles est la même au fil du temps et quelle est l'ampleur de la variabilité.

Utiliser la fonction map ici est plutôt simple, il nous suffit d'ajouter une autre liste en entrée.

Pour cette tâche, on va utiliser l'action qu'on vient de traiter et une nouvelle, les relevés de Tesla en EUR.

Voici la nouvelle liste d'action :

python
tesla_eur = [96.32, 89.44, 77.4, 98.9, 83.42, 106.64, 110.08, 100.62, 110.94, 90.3, 106.64, 85.14, 99.76, 99.76, 77.4, 92.02, 92.88, 92.88, 81.7, 90.3, 87.72, 81.7, 83.42, 83.42, 82.56, 100.62, 106.64, 86.86, 103.2, 92.02, 96.32, 100.62, 80.84, 83.42, 81.7, 105.78, 79.12, 104.06, 108.36, 104.92, 88.58, 103.2, 110.08, 104.92, 91.16, 93.74, 95.46, 110.94, 81.7, 80.84, 92.02, 88.58, 102.34, 97.18, 80.84, 102.34, 93.74, 90.3, 89.44, 83.42, 109.22, 110.08, 92.88, 91.16, 100.62, 78.26, 105.78, 92.88, 105.78, 86.0, 103.2, 111.8, 110.08, 110.94, 92.02, 109.22, 77.4, 82.56, 107.5, 82.56, 78.26, 105.78, 110.08, 80.84, 109.22, 99.76, 93.74, 107.5, 85.14, 81.7, 98.04, 91.16, 104.06, 89.44, 89.44, 102.34, 111.8, 109.22, 105.78, 95.46]

D'abord, définissons une fonction qui reçoit deux listes en entrée et renvoie une liste où chaque élément est la différence entre les éléments des deux listes à la même position.

python
def difference_calculator (stock1, stock2):
  # This function will only do the difference between to values, the map function will reiterate it for the whole length of the lists
  return round(stock1 - stock2, 2)

Maintenant, utilisons la fonction map pour traiter les listes. Dans ce cas, on utilise la même structure qu'avant, en ajoutant simplement la deuxième liste après la première. Notre fonction prend maintenant deux arguments, on fournit deux listes, tout va bien.

python
difference_values = list(map(difference_calculator, amazon_eur_ml, tesla_eur))
print(difference_values)

Et maintenant, on peut terminer notre tâche en calculant la variabilité ?

hidden
Click to reveal|||python|||Hide|||1|||
def mean(lst):
  return sum(lst)/len(lst)

def standard_deviation(lst):
  m = mean(lst)
  return round((sum([(v - m)**2 for v in lst])/len(lst)) ** 0.5, 2)

print(mean(difference_values), standard_deviation(difference_values))
print('Would you consider this high variability or low variability?')

La fonction Filter

Ton boss vient de te féliciter pour ta solution au défi de la variabilité ! Félicitations ! Il est tellement content et il a hâte de voir ta solution pour cette nouvelle tâche : tu as une liste de tuples, dans chaque tuple il y a le nom de l'action et les prix moyens journaliers de l'action. Ton boss a juste besoin des actions dont le prix journalier est inférieur à 100.

Pour accomplir cette tâche, on va utiliser la fonction filter. La structure de la fonction est la suivante :

output_variable = filter(condition_function, input_list1, input_list2, ...)

Si la fonction de condition renvoie True, la valeur de la liste est gardée, sinon elle est écartée.

Ici se trouve la documentation de la fonction filter.

Ici tu peux trouver notre liste de prix journaliers :

python
daily_prices = [('BAC', 45.07), ('PLUG', 31.81), ('AAPL', 143.76), ('AMC', 40.07), ('WFC', 45.31), ('T', 25.62), ('F', 15.45), ('AMD', 111.99)]

Maintenant, utilisons la fonction filter pour construire le code. Écrivons notre fonction qui prend un tuple en entrée et renvoie une variable booléenne. Si la valeur est inférieure à 100, la fonction renvoie True, sinon False.

python
def is_lower_than_100(tuple_stock):
  return tuple_stock[1] < 100

Maintenant il ne reste plus qu'à appliquer la fonction filter :

python
print(filter(is_lower_than_100, daily_prices))

Oups ! On a de nouveau un objet. Peux-tu me dire comment tu vas régler ça ?

hidden
Click to reveal|||python|||Hide|||1|||
print(list(filter(is_more_than_100, daily_prices)))

Génial, ça a marché !! 🎉 YOUPI !! 🎉 🎉

Reduce

Pour notre dernière tâche, on nous a demandé de calculer des probabilités, donc on a besoin de créer une fonction factorielle. Comme c'est presque l'heure de rentrer à la maison, on a beau se gratter la tête, on n'arrive pas à se souvenir comment écrire la fonction avec la structure où une fonction s'appelle elle-même depuis l'intérieur de la fonction. Alors, puisqu'il est tard, on décide simplement de l'écrire avec la fonction reduce.

La fonction reduce est une fonction qui parcourt la liste de façon itérative et traite le résultat de la fonction appliquée à la valeur précédente et à une nouvelle valeur. La structure de la fonction reduce est la suivante :

output_variable = reduce(function, input_list)

La fonction doit prendre deux variables en entrée, dont l'une est cumulative.

Ici se trouve la documentation.

python
from functools import reduce

def factorial(n, m):
  #The basic operation of the factorial is to multiply one value with another
  return n * m

fact = reduce(factorial, list(range(1, 6)))
print(fact)

Dans ce cas, à la première itération on multiplie 1 * 2 = 2 À la deuxième itération on multiplie le résultat précédent 2 et la nouvelle valeur 3 : 2 * 3 = 6 À la troisième itération on multiplie le résultat précédent 6 et la nouvelle valeur 4 : 6 * 4 = 24 À la quatrième itération on multiplie le résultat précédent 24 et la nouvelle valeur 5 : 24 * 5 = 120

Que se serait-il passé si j'avais utilisé range(6) ❓

hidden
Click to reveal|||text|||Hide|||1|||
It would have had multiplied 0 * 1 * 2 * 3 * 4 * 5 = 0

Et c'était la dernière des nouvelles fonctions. Plus tard, tu pourras te détendre et célébrer une bonne journée de travail avec une part de pizza et un bon vin / jus / boisson de ton choix. Mais ne pars pas tout de suite, un magnifique défi t'attend !

Ressources supplémentaires

↗
https://book.pythontips.com/en/latest/map_filter.html

Chouette article sur map, filter et reduce

book.pythontips.com
↗
https://www.youtube.com/watch?v=hUes6y2b--0

Vidéo expliquant le sujet

youtube.com

Défi

Bravo d'être arrivé jusqu'ici ! 🎉🎉🎉🎉

C'est l'heure du défi !

image.

Copie ce notebook dans ton Drive et complète-le. Une fois que tu as fini, poste un lien vers ton notebook comme solution pour validation, n'oublie pas de le partager pour que quiconque ayant le lien puisse le lire.

Critères de validation

  • Le notebook est partagé de façon à être lisible par quiconque possède le lien
  • Les exercices ont été correctement codés

Anyone tired of loops yet? 😉

hermione raise hand

Well, look no further my friend! The map, filter, and reduce functions are here to help you kick those pesky for-loops off your code.

How are they going to do that? Let's see.

To understand this quest you need to know:

Content 🤓

By the end of this quest, you will:

✅ be able to manipulate all the values of one or multiple lists at once by using the map function ✅ be able to filter a list at once by using the filter function ✅ be able to perform calculations that requires the whole list by using the reduce function

Map Function

Processing a Single List

Imagine yourself at work, working in the Financial Sector. For your first task, you need to change all the USD prices in EUR values.

This are the values to change:

python
amazon_usd = [213, 217, 221, 348, 331, 290, 277, 273, 209, 335, 240, 280, 278, 207, 304, 309, 204, 292, 261, 265, 226, 242, 262, 304, 250, 296, 202, 281, 294, 244, 231, 204, 275, 350, 242, 251, 312, 276, 219, 246, 214, 337, 273, 239, 218, 252, 324, 212, 232, 214, 220, 205, 339, 347, 275, 290, 345, 234, 338, 206, 328, 230, 209, 301, 291, 332, 348, 281, 208, 283, 328, 232, 200, 248, 316, 204, 242, 265, 300, 209, 257, 217, 347, 220, 260, 305, 221, 257, 281, 216, 329, 207, 205, 313, 321, 333, 231, 273, 323, 245]

First, let's write the function to turn USD values into EUR. Let's assume the current exchange rate is 1 USD = 0.86 EUR

python
def usd_to_eur (money):
  return round(money * 0.86, 2)

You just finished coding your function. You have a whole list of variables you have to pass through your function. Maybe you remember the list comprehension quest, maybe you don't. Maybe you only remember how to do the basic for loop. All good, we can make it with just one for-loop:

python
amazon_eur_fl = [] 
for m_usd in amazon_usd:
  m_eur = usd_to_eur(m_usd)
  amazon_eur_fl.append(m_eur)


print('This are the EUR values of your stock with for loops.')
print(amazon_eur_fl)

If you do remember list comprehensions, you might want to use them:

python
amazon_eur_lc = [usd_to_eur(m_usd) for m_usd in amazon_usd] 

print('This are the EUR values of your stock with list comprehensions.')
print(amazon_eur_lc)

It already makes for a shorter code. it's an improvement, is it not?

By using the map function will allow for an even clearer and shorter code while still achieving the same result. This is its structure:

output_variable = map(function, input_list1, input_list2, ...)

Here is the map function documentation.

In our task - change the values from USD to EUR - we have our function and one input list. The map function is a built-in function of Python, so no need to worry about importing anything.

In our case the function's name is usd_to_eur, the input variable is amazon_usd and the output variable it amazon_eur_m

python
amazon_eur_m = map(usd_to_eur, amazon_usd)
print(amazon_eur_m)

Now, small problem: does it work? No clue yet - at least it didn't raise an error, but we cannot see what's inside: the map function gives back an object!! More precisely, a map iterator, an abstract sequence of results. We need a container to put those results in! So now what?

hidden
Click to reveal|||python|||Hide|||1|||
amazon_eur_ml = list(map(usd_to_eur, amazon_usd))
print(amazon_eur_ml)

Congratulations! It was easy, wasn't it?

Processing Multiple Lists

We made it to the change of currency. Now we need to compare two different stocks to learn about their variability. We want to know if the difference between them is the same over time and how much variability there is.

To use the map Function here is quite easy, we just need to add another list as input variable.

For this task we'll use the stock we just processed and a new one, the records from Tesla in EUR.

This is the new stock list:

python
tesla_eur = [96.32, 89.44, 77.4, 98.9, 83.42, 106.64, 110.08, 100.62, 110.94, 90.3, 106.64, 85.14, 99.76, 99.76, 77.4, 92.02, 92.88, 92.88, 81.7, 90.3, 87.72, 81.7, 83.42, 83.42, 82.56, 100.62, 106.64, 86.86, 103.2, 92.02, 96.32, 100.62, 80.84, 83.42, 81.7, 105.78, 79.12, 104.06, 108.36, 104.92, 88.58, 103.2, 110.08, 104.92, 91.16, 93.74, 95.46, 110.94, 81.7, 80.84, 92.02, 88.58, 102.34, 97.18, 80.84, 102.34, 93.74, 90.3, 89.44, 83.42, 109.22, 110.08, 92.88, 91.16, 100.62, 78.26, 105.78, 92.88, 105.78, 86.0, 103.2, 111.8, 110.08, 110.94, 92.02, 109.22, 77.4, 82.56, 107.5, 82.56, 78.26, 105.78, 110.08, 80.84, 109.22, 99.76, 93.74, 107.5, 85.14, 81.7, 98.04, 91.16, 104.06, 89.44, 89.44, 102.34, 111.8, 109.22, 105.78, 95.46]

First, let's define a function that receives two lists in input and returns a list where each item is the difference between the items from the two lists in the same position.

python
def difference_calculator (stock1, stock2):
  # This function will only do the difference between to values, the map function will reiterate it for the whole length of the lists
  return round(stock1 - stock2, 2)

Now let's use the map function to process the lists. In this case we are using the same structure as before, in addition we are just adding the second list after the first. Our function now takes two arguments, we're providing two lists, everything's fine.

python
difference_values = list(map(difference_calculator, amazon_eur_ml, tesla_eur))
print(difference_values)

And now we can finish our task calculating the variability?

hidden
Click to reveal|||python|||Hide|||1|||
def mean(lst):
  return sum(lst)/len(lst)

def standard_deviation(lst):
  m = mean(lst)
  return round((sum([(v - m)**2 for v in lst])/len(lst)) ** 0.5, 2)

print(mean(difference_values), standard_deviation(difference_values))
print('Would you consider this high variability or low variability?')

Filter Function

Your boss just congratulated you on your solution to the variability challenge! Congratulations! He is so happy and looking forward to your solution of this new task: you have a list of tuples, in each tuple there is the name of the stock and the daily average stock prices. Your boss just needs the stocks with a daily price lower than 100.

To accomplish this task, we are going to use the filter function. The structure of the function is the following one:

output_variable = filter(condition_function, input_list1, input_list2, ...)

If the condition function returns True, the value from the list is kept, otherwise it is discarded.

Here is the filter function documentation.

Here you can find our daily price list:

python
daily_prices = [('BAC', 45.07), ('PLUG', 31.81), ('AAPL', 143.76), ('AMC', 40.07), ('WFC', 45.31), ('T', 25.62), ('F', 15.45), ('AMD', 111.99)]

Now let's use the filter function to build the code. Let's write our function that takes a tuple as input and returns a boolean variable. If the value is lower than 100, the function returns True, otherwise False.

python
def is_lower_than_100(tuple_stock):
  return tuple_stock[1] < 100

Now all is left is to apply the filter function:

python
print(filter(is_lower_than_100, daily_prices))

Oops! We have an object again. Can you tell me how you are going to fix this?

hidden
Click to reveal|||python|||Hide|||1|||
print(list(filter(is_more_than_100, daily_prices)))

Awesome, it worked!! 🎉 YAY!! 🎉 🎉

Reduce

For our last task, we have been asked to calculate some probabilities, so we need to create a factorial function. Since it's almost time to go home, for as much as we scratch our head, we are not remembering how to write the function using the structure in which a function calls itself from inside the function. So, since it's late, we just decide to write it using the reduce function.

The reduce function is a function that goes iteratively through the list and processes the result of the function up to the previous value and a new value. The structure of the reduce function is the following one:

output_variable = reduce(function, input_list)

The function has to take two input variables, one of which is cumulative.

Here is the documentation.

python
from functools import reduce

def factorial(n, m):
  #The basic operation of the factorial is to multiply one value with another
  return n * m

fact = reduce(factorial, list(range(1, 6)))
print(fact)

In this case on the first iteration we multiply 1 * 2 = 2 On the second iteration we multiply the previous result 2 and the new value 3: 2 * 3 = 6 On the third iteration we multiply the previous result 6 and the new value 4: 6 * 4 = 24 On the fourth iteration we multiply the previous result 24 and the new value 5: 24 * 5 = 120

What would have happened if I used range(6)❓

hidden
Click to reveal|||text|||Hide|||1|||
It would have had multiplied 0 * 1 * 2 * 3 * 4 * 5 = 0

And that was the last of the new functions. Later on you can relax and celebrate a good day of work with a slice of pizza and good wine/juice/beverage of choice. But don't go yet, a wonderful challenge is waiting for you!

Additional Resources

↗
https://book.pythontips.com/en/latest/map_filter.html

Nice post about map, filter, and reduce

book.pythontips.com
↗
https://www.youtube.com/watch?v=hUes6y2b--0

Video explaining the topic

youtube.com

Challenge

Great job arriving here! 🎉🎉🎉🎉

Now is challenge time!

image.

Please copy this notebook in your Drive and complete it. Once you are finished, post a link to your notebook as a solution for validation, don't forget to share it so that anyone who has the link can read it.

Validation Criteria

  • The notebook is shared so that is readable by anyone with the link
  • The exercises have been correctly coded