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
Mapa, Filtro, Reduce
Map, Filter y Reduce son paradigmas de la programación funcional. Permiten al programador (tú) escribir código más simple y corto, sin necesidad de preocuparse necesariamente por detalles como bucles y ramificaciones.
Esencialmente, estas tres funciones te permiten aplicar una función a una serie de iterables de una sola vez. map
y filter
vienen integrados con Python (en el módulo __builtins__
) y no requieren importación. reduce
, sin embargo, necesita ser importado ya que reside en el módulo functools
. Vamos a obtener una mejor comprensión de cómo funcionan todos, comenzando con map
.
Map
La función map()
en Python tiene la siguiente sintaxis:
map(func, *iterables)
Donde func
es la función que se aplicará a cada elemento en iterables
(tantos como sean). ¿Notaste el asterisco (*
) en iterables
? Significa que puede haber tantos iterables como sea posible, siempre que func
tenga exactamente ese número de argumentos de entrada requeridos. Antes de pasar a un ejemplo, es importante que notes lo siguiente:
- En Python 2, la función
map()
devuelve una lista. En Python 3, sin embargo, la función devuelve unmap object
, que es un objeto generador. Para obtener el resultado como una lista, se puede llamar a la función incorporadalist()
en el objeto mapa, es decir,list(map(func, *iterables))
. - El número de argumentos para
func
debe ser el número deiterables
listados.
Veamos cómo se aplican estas reglas con los siguientes ejemplos.
Digamos que tengo una lista (iterable
) de los nombres de mis mascotas favoritas, todos en minúsculas, y los necesito en mayúsculas. Tradicionalmente, en programación normal en Python, haría algo así:
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []
for pet in my_pets:
pet_ = pet.upper()
uppered_pets.append(pet_)
print(uppered_pets)
Lo cual produciría ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
.
Con las funciones map()
, no solo es más fácil, sino que también es mucho más flexible. Simplemente hago esto:
# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
Que también produciría el mismo resultado. Ten en cuenta que al usar la sintaxis definida de map()
arriba, func
en este caso es str.upper
y iterables
es la lista my_pets
-- solo un iterable. También observa que no llamamos a la función str.upper
(haciendo esto: str.upper()
), ya que la función map lo hace por nosotros en cada elemento en la lista my_pets
.
Lo que es más importante notar es que la función str.upper
requiere solo un argumento por definición, así que pasamos solo un iterable a ella. Entonces, si la función que estás pasando requiere dos, o tres, o n argumentos, entonces necesitas pasar dos, tres o n iterables a ella. Permíteme aclararlo con otro ejemplo.
Digamos que tengo una lista de áreas de círculos que calculé en algún lugar, todas con cinco decimales. Y necesito redondear cada elemento en la lista a su posición decimal, lo que significa que tengo que redondear el primer elemento de la lista a un decimal, el segundo elemento de la lista a dos decimales, el tercer elemento de la lista a tres decimales, etc. Con map()
esto es pan comido. Veamos cómo.
Python ya nos bendice con la función incorporada round()
que toma dos argumentos: el número a redondear y el número de decimales a los que se redondeará el número. Entonces, como la función requiere dos argumentos, necesitamos pasar dos iterables.
# 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)
¿Ves la belleza de map()
? ¿Puedes imaginar la flexibilidad que esto evoca?
La función range(1, 7)
actúa como el segundo argumento para la función round
(el número de decimales requeridos por iteración). Así que mientras map
itera a través de circle_areas
, durante la primera iteración, el primer elemento de circle_areas
, 3.56773
se pasa junto con el primer elemento de range(1,7)
, 1
a round
, haciéndolo efectivamente round(3.56773, 1)
. Durante la segunda iteración, el segundo elemento de circle_areas
, 5.57668
junto con el segundo elemento de range(1,7)
, 2
se pasa a round
, haciéndolo traducir a round(5.57668, 2)
. Esto sucede hasta llegar al final de la lista circle_areas
.
Estoy seguro que te estás preguntando: "¿Qué pasa si paso un iterable menor o mayor que la longitud del primer iterable? Es decir, si paso range(1, 3)
o range(1, 9999)
como el segundo iterable en la función anterior". Y la respuesta es simple: ¡nada! Bueno, eso no es del todo cierto. "Nada" en el sentido de que la función map()
no emitirá ninguna excepción, simplemente iterará sobre los elementos hasta que no encuentre un segundo argumento para la función, momento en el cual simplemente se detiene y devuelve el resultado.
Así que, por ejemplo, si evalúas result = list(map(round, circle_areas, range(1, 3)))
, no obtendrás ningún error incluso si la longitud de circle_areas
y la longitud de range(1, 3)
difieren. En cambio, esto es lo que hace Python: Toma el primer elemento de circle_areas
y el primer elemento de range(1,3)
y lo pasa a round
. round
lo evalúa y luego guarda el resultado. Luego pasa a la segunda iteración, segundo elemento de circle_areas
y segundo elemento de range(1,3)
, round
lo guarda de nuevo. Ahora, en la tercera iteración (circle_areas
tiene un tercer elemento), Python toma el tercer elemento de circle_areas
y luego intenta tomar el tercer elemento de range(1,3)
pero dado que range(1,3)
no tiene un tercer elemento, Python simplemente se detiene y devuelve el resultado, que en este caso sería simplemente [3.6, 5.58]
.
Adelante, pruébalo.
# 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)
Lo mismo ocurre si circle_areas
es menor que la longitud del segundo iterable. Python simplemente se detiene cuando no puede encontrar el siguiente elemento en uno de los iterables.
Para consolidar nuestro conocimiento de la función map()
, vamos a usarla para implementar nuestra propia función zip()
. La función zip()
es una función que toma varios iterables y luego crea una tupla que contiene cada uno de los elementos en los iterables. Al igual que map()
, en Python 3, devuelve un objeto generador, que puede convertirse fácilmente en una lista llamando a la función incorporada list
sobre él. Usa la siguiente sesión de intérprete para familiarizarte con zip()
antes de crear el nuestro con 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)
Como un bonus, ¿puedes adivinar qué sucedería en la sesión anterior si my_strings
y my_numbers
no tienen la misma longitud? ¿No? Inténtalo! Cambia la longitud de uno de ellos.
¡Vamos a nuestra propia función zip()
!
# 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)
¡Sólo mira eso! Tenemos el mismo resultado que zip
.
¿También notaste que ni siquiera necesitaba crear una función usando el método estándar def my_function()
? ¡Así de flexible es map()
, y Python en general! Simplemente usé una función lambda
. Esto no quiere decir que no se permita usar el método estándar de definición de funciones (de def function_name()
), aún está permitido. Simplemente preferí escribir menos código (ser "Pythonic").
Eso es todo sobre map. Pasemos a filter()
Filter
Mientras que map()
pasa cada elemento en el iterable a través de una función y devuelve el resultado de todos los elementos que han pasado por la función, filter()
, en primer lugar, requiere que la función devuelva valores booleanos (verdadero o falso) y luego pasa cada elemento en el iterable a través de la función, "filtrando" aquellos que son falsos. Tiene la siguiente sintaxis:
filter(func, iterable)
Se deben tener en cuenta los siguientes puntos sobre filter()
:
- A diferencia de
map()
, solo se requiere un iterable. - El argumento
func
debe devolver un tipo booleano. Si no lo hace,filter
simplemente devuelve eliterable
que se le pasó. Además, como solo se requiere un iterable, es implícito quefunc
debe tomar solo un argumento. filter
pasa cada elemento en el iterable a través defunc
y devuelve solo aquellos que se evalúan como verdaderos. Quiero decir, está justo en el nombre: un "filtro".
Veamos algunos ejemplos
La siguiente es una lista (iterable
) de las calificaciones de 10 estudiantes en un examen de Química. Filtraremos aquellos que aprobaron con calificaciones superiores a 75... usando 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)
El siguiente ejemplo será un detector de palíndromos. Un "palíndromo" es una palabra, frase o secuencia que se lee igual hacia adelante y hacia atrás. Filtraremos palabras que son palíndromos de una tupla (iterable
) de palíndromos sospechosos.
# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
Lo cual debería producir ['madam', 'anutforajaroftuna']
.
Bastante limpio, ¿verdad? Finalmente, reduce()
Reduce
reduce
aplica una función de dos argumentos de manera acumulativa a los elementos de un iterable, comenzando opcionalmente con un argumento inicial. Tiene la siguiente sintaxis:
reduce(func, iterable[, initial])
Donde func
es la función en la cual cada elemento en el iterable
se aplica de manera acumulativa, y initial
es el valor opcional que se coloca antes de los elementos del iterable en el cálculo, y sirve como valor predeterminado cuando el iterable está vacío. Debes tener en cuenta lo siguiente sobre reduce()
:
1. func
requiere dos argumentos, el primero de los cuales es el primer elemento en iterable
(si initial
no está suministrado) y el segundo elemento en iterable
. Si se suministra initial
, entonces se convierte en el primer argumento de func
y el primer elemento en iterable
se convierte en el segundo elemento.
2. reduce
"reduce" (lo sé, perdóname) iterable
en un solo valor.
Como de costumbre, veamos algunos ejemplos.
Vamos a crear nuestra propia versión de la función incorporada sum()
de Python. La función sum()
devuelve la suma de todos los elementos en el iterable que se le pasa.
# 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)
El resultado, como esperarías, es 68
.
Entonces, ¿qué sucedió?
Como de costumbre, todo se trata de iteraciones: reduce
toma el primero y el segundo elemento en numbers
y los pasa a custom_sum
respectivamente. custom_sum
calcula su suma y la devuelve a reduce
. reduce
luego toma ese resultado y lo aplica como el primer elemento a custom_sum
y toma el siguiente elemento (tercero) en numbers
como el segundo elemento para custom_sum
. Hace esto continuamente (de manera acumulativa) hasta que numbers
se agota.
Veamos qué sucede cuando utilizo el valor opcional initial
.
# 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)
El resultado, como esperarías, es 78
porque reduce
, inicialmente, utiliza 10
como el primer argumento de custom_sum
.
Eso es todo sobre Map, Reduce, y Filter en Python. Intenta los ejercicios a continuación para ayudar a afirmar tu comprensión de cada función.
Exercise
En este ejercicio, usarás cada uno de map
, filter
, y reduce
para arreglar el código roto.
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!