具有多个迭代器的映射

到目前为止,我们一直向 map 函数传递一个可迭代对象,以及一个需要单个参数的函数,以便将来自该可迭代对象的各个项目传递给后续的函数调用。

我们还可以定义一个带有多个参数的函数,每个参数都可以来自一个单独的可迭代对象。

让我们定义一个函数,它接受两个数字并返回它们的 GCD 或者“最大公约数”。

def gcd(a,b):
    if a < b:
        a,b = b,a
    while(b!=0):
        a, b = b, a%b
    return a
print(f"gcd of 45 and 30 is {gcd(45,30)}")

我们将定义两个长度相等的独立列表,并将它们与计算 gcd 的方法一起传递给 map函数。

map 函数将迭代调用gcd(a,b)方法,其第一个参数将从第一个列表中获取,第二个参数将从第二个列表中获取。

x = [45, 3, 18, 27, 37]
y = [30, 5, 12, 81, 9]
print(f"x: {x}")
print(f"y: {y}")
gcds = map(gcd, x, y) # calling map with more than 1 iterables
gcds = list(gcds)
print(f"GCDs: {gcds}")

请注意,两个列表必须具有相同的长度,因为参数是成对传递给函数的,两个列表各有一个。

如果两个列表的长度不同,则将处理最小可能的对,并丢弃较长列表中的另外元素。
在这种情况下,结果的长度将与较小列表的长度匹配。

请注意,我们传递给 map 的两个(或者更多)可迭代对象不一定必须是同一类型。

这意味着如果一个 iterable 是一个列表,另一个可能是一个元组,第三个可能是一个集合,依此类推。

让我们定义一个函数,它接受一个学生的名字(string)的 3 个值,他们的卷号。
( int) 和 cgpa ( float),并返回一个字典,其中每个项目都用它们的键标记。

我们将把这个函数和 3 个可迭代对象一起传递给 map函数。

import numpy as np
def get_student_dict(name, roll, cgpa):
    student = {
        "name": name,
        "roll no.": roll,
        "CGPA": cgpa
    }

    return student

names = ["Adam", "Becka", "Brandon", "Charlotte", "Mariyam"] # a list(length=5)
roll_nos = (1, 2, 3, 4, 5) # a tuple(length=5)
cgpa = np.array([9.2, 7.6, 8.5, 9.8, 8.7, 4.8]) # a NumPy array(length=6)
print(f"names = {names}, type={type(names)}\n")
print(f"roll_nos = {roll_nos}, type={type(roll_nos)}\n")
print(f"cgpa = {cgpa}, type={type(cgpa)}\n")
student_dicts = map(get_student_dict, names, roll_nos, cgpa)
print("Student dictionaries:\n")
for student in student_dicts:
    print(f"{student}\n")

这里有几点需要注意:

  • 我们传递给 map 的三个可迭代对象各有不同的类型——列表、元组和 NumPy 数组。
  • 这些可迭代对象的长度不相等,cgpa数组有一个另外的值,该值被 map丢弃。
  • 我们不会将返回的映射对象转换为列表或者元组。由于它是可迭代的,我们直接使用 for循环对其进行迭代。

使用 lambda 中的条件映射

在上一节中,我们讨论了不能在 lambda 函数中使用普通的 Python 语句,我们必须将返回值表示为表达式。

但是,如果我们必须使用 if..else 构造,我们可以使用以下语法将它们包含为表达式的一部分:
lambda args: val1 if condition else val2

让我们通过定义一个 lambda 函数来查找给定值是否为偶数来理解这一点。
我们可以使用 map在数字列表上使用它。

结果将是一个布尔值列表,指示列表中的相应值是否为偶数。

a = [13, 60, 0, 2, 17, 19]
print(f"a = {a}\n")
is_even = list(map(lambda x: True if x%2==0 else False, a))
print(f"is_even(a) = {is_even}")

用 lambda 映射

到目前为止,我们一直在调用map函数之前预先定义要传递的函数。

但是 Python 映射函数的真正潜力在它与 lambda函数一起使用时得以实现。

让我们首先了解什么是“lambda”。

lambda是一个 Python 关键字,用于创建匿名函数。

匿名函数,顾名思义,就是没有名字的函数。

我们使用 def关键字定义函数的典型方法包括使用名称声明函数。
我们只需要定义一次这样的函数,并且我们可以在程序的不同位置根据需要多次使用它。

另一方面,匿名函数是在没有名称的情况下构造的,通常不打算在多个位置重复使用。

lambda 函数声明的语法是:lambda arg1, arg2,... : expression
一个 lambda 函数可以接受 1 个以上的参数,但它的返回值必须是一个表达式。
这意味着,在返回值之前,它不能像普通函数那样有多个 Python 语句。

让我们定义一个 lambda 函数来求值的平方。

square = lambda x: x**2
print(f"Square of 12 = {square(12)}")

请注意,lambda 函数没有任何显式的 return 语句,我们指定的“表达式”由函数计算并返回。

另请注意,虽然我们已将 lambda 函数分配给名为“square”的变量,但没有必要这样做,这里只是为了方便起见。

我们可以很好地定义一个 lambda 函数并同时调用它,而无需将其分配给任何变量。

x = (lambda x: x**2)(25) #creating and calling lambda in single step
print(f"square of 25 = {x}")

Lambda 函数在我们必须将函数对象作为参数传递给其他函数时特别有用,例如在 map的情况下。

现在让我们使用 lambda 函数调用 map来计算列表中所有数字的平方根。

a = [144, 25, 400, 81, 36]
print(f"a = {a}")
square_roots = map(lambda x: x**(0.5), a) #using lambda to compute square roots
square_roots = list(square_roots)
print(f"square roots = {square_roots}")

让我们也举一个带有多个参数的 lambda 的例子。

我们将定义一个接受两个参数并返回它们的乘积的 lambda。

然后我们将在带有两个列表的 map函数中使用它,以找到两个列表中值的成对乘积。

a = [1, 2, 3, 4, 5]
b = [10, 20, 30, 40, 50]
print(f"a = {a}")
print(f"b = {b}")
products = list(map(lambda x,y: x*y, a, b))
print(f"products = {products}")

元组上的映射函数

如前所述,可以在任何有效的 Python 可迭代对象上调用 map方法,例如元组、字符串、字典、NumPy 数组等。

让我们举一个例子来说明它在元组上的用法。

在这里,我们将使用 map函数将 str.lower方法应用于以字符串形式存储在元组中的一堆名称。

names = ("John", "Adam", "STANLEY", "toNy", "Alisha")
print(f"names: {names}")
names_lower = list(map(str.lower, names))
print(f"names in lowercase: {names_lower}")

这里我们没有像之前那样传递用户定义的函数。
我们改为在 Python 中传递 string 模块的内置方法。

请注意,我们必须将函数名称(不带括号)而不是函数调用作为第一个参数传递给 map

NumPy 数组上的映射函数

由于 NumPy 数组是可迭代的,我们也可以对它们应用 map 函数。

默认情况下,在多维数组的情况下,map会将函数应用于沿第一个轴的 NumPy 数组。

让我们首先将 map应用于一维数组。
我们将使用 map来查找 NumPy 数组中每个元素的平方。
我们将使用 NumPy 随机种子,因此我们可以生成相同的随机数。

import numpy as np
np.random.seed(42)
def square(x):
    return x**2
a = np.random.randint(1,10, size=(6,))
print(f"array a = {a}")
a_squared = tuple(map(square, a))
print(f"squares of a = {a_squared}")

映射嵌套列表

到目前为止,调用函数的可迭代对象中的各个值是单个标量值(或者在二维数组输入的情况下是 NumPy 数组)。

我们还可以将 map函数应用于嵌套列表。
在这里,我们传递给 map的函数将接受一个列表(或者元组)作为其参数。

让我们考虑一个个人姓名列表。
这些名称不会存储为单个字符串。

相反,它们将被定义为 2 个字符串的列表,其中第一个将存储名字,列表中的第二个元素将是姓氏。

names = [["Stephen", "Hawking"],
         ["John", "Doe"],
         ["Christian", "Wolf"],
         ["Aditi", "Singh"],
         ["Maria", "Pereira"]]
print(f"{'First Name':10} {'Last Name':10}")
for name in names:
    print(f"{name[0]:10} {name[1]:10}")

我们将定义一个函数,该函数接受一个包含名字和姓氏的列表,并返回一个表示个人全名的连接字符串。

我们将使用 map将此函数应用于我们上面定义的列表中的所有名称。

def get_full_name(name):
    return " ".join(name)
full_names = list(map(get_full_name, names))
print(f"full names: {full_names}")

字典上的映射

我们已经在列表、元组和 NumPy 数组上看到了 map的用法。
现在让我们了解如何利用该函数来处理字典。

迭代字典不像列表、元组等那样简单,因为字典存储键值对的集合。

如果我们使用 for 循环迭代字典变量,则迭代器变量将在每次迭代期间分配一个字典的键。

让我们通过定义一个字典 electricity_bills来理解这一点,它的键是电力客户的消费者 ID,值是包含消费者姓名和过去 6 个月电费金额列表的元组。

electricity_bills = {
     11001: ("Pete Wolfram",[100, 85, 200, 150, 96, 103]),
     11002: ("Jessica Becker", [76, 88, 102, 97, 68, 72]),
     11003: ("Alex Jasper",[170, 190, 165, 210, 195, 220]),
     11004: ("Irina Ringer",[350, 284, 488, 372, 403, 410]),
     11005: ("Sean Adler",[120, 115, 111, 109, 121, 113])

 }

for k in electricity_bills:
    print(k)

如果我们直接迭代它,我们只能访问字典的键。
map函数将展示类似的行为。

我们传递给 map的函数将仅使用字典的键进行迭代调用。

但在这里我们也想处理字典的值。
因此,我们传递给 map函数的函数应该接收字典中的键和值。

我们可以通过在字典上调用items方法并使用与map函数相同的iterable 而不是直接使用字典来实现这一点。

items方法返回一个 dict_items对象,该对象将字典的键值对作为列表中的元组。

让我们定义一个函数,它接受这样的键值对,计算客户的平均每月账单,并返回一个包含消费者 ID 和每月平均账单的元组。

然后我们将使用这个函数和 map来查找字典中所有客户的平均账单。

def calculate_average_bill(consumer):
    # consumer is a tuple having key-value pair
    key, value = consumer
    consumer_id = key
    bill_amounts = value[1]
    avg_bill = sum(bill_amounts)/len(bill_amounts)

    return(consumer_id, round(avg_bill,4))

average_bills = list(map(calculate_average_bill, electricity_bills.items()))
print(f"average monthly bills: {average_bills}")

因此,我们得到了一个元组列表,每个元组都有 consumer_id 和平均每月账单。

如果我们只想处理它们的值,我们可以类似地调用字典上的 values()函数。

在 2D NumPy 数组上映射

现在让我们使用 map来查找 2D NumPy 数组中每一行的算术平均值。

如果我们迭代多维数组,那么默认情况下,它会在轴 0 上迭代,在二维数组的情况下,它是行轴。

map函数表现出相同的行为,它将提供的函数迭代地应用于数组中的每一行。

np.random.seed(42)
def get_mean(x):
    m = sum(x)/len(x)
    return round(m,4)
b = np.random.randint(1,100, size=(4,6))
print(f"array b:\n {b}\n")
means = list(map(get_mean, b))
print(f"row-wise means: {means}")

我们得到 4 个平均值,数组 b 中的每一行一个。

如果要按列应用函数,可以将数组 b 的转置作为第二个参数传递给 map

col_means = list(map(get_mean, b.T))
print(f"column-wise means of b: {col_means}")

或者,我们可以使用 NumPy 的 apply_along_axis沿 NumPy 数组的任何轴应用函数。

要找到 b 列的均值,我们应该沿轴 0 应用 get_axis

col_means = np.apply_along_axis(get_mean, 0, b)
print(f"column-wise means using NumPy = {col_means}")

该方法将结果以 NumPy 数组形式返回。

Python map() 函数

我们是否一直在使用 for循环对 Python 中的项目列表执行重复性任务?

我们是否希望存在一种更有效的方法来将函数应用于 Python 列表中的每个项目?

如果回答是肯定的,那么我们还没有在 Python 中发现一个重要而强大的工具 map()函数。

在本教程中,我们将揭示 map 函数的功能,它不仅可以帮助我们实现比 for 循环更高效的迭代,还可以帮助我们编写更干净的代码。

map函数有什么作用?

map函数帮助我们迭代地将函数应用于 Python 列表中的所有项目,或者任何 Python 可迭代的项目,只需一行代码。

map函数接受两个参数,第一个参数是应用于可迭代对象(列表、元组、集合等)中的单个项目的函数,第二个参数是可迭代对象本身。

map函数返回一个映射对象,可以通过调用适当的方法将其转换为所需的可迭代对象(列表、元组、集合等)。

让我们考虑一个 Python 函数,将华氏温度转换为其等效摄氏温度。

我们将把这个函数应用到从不同城市收集的温度列表。

def fahrenheit_to_celcius(F):
    C = (F-32)*(5/9)
    return round(C,4)
temp_fahrenheit = [100, 95, 98, 105, 110, 32]
temp_celcius =  []
for tf in temp_fahrenheit:
    tc = fahrenheit_to_celcius(tf)
    temp_celcius.append(tc)

print(f"Temperatures in Fahrenheit: {temp_fahrenheit}")
print(f"Temperatures converted to Celcius: {temp_celcius}")

在这里,我们采用了迭代列表的传统方法,例如:使用 for 循环。

我们首先创建了一个空列表 temp_celcius,然后在 for 循环中,我们访问列表 temp_fahrenheit中的每个项目。

我们在这些项目上调用方法 fahrenheit_to_celcius并将结果添加到 temp_celcius

让我们看看如何通过对“map”函数的一次调用来代替这两个步骤。

temp_celcius_map = list(map(fahrenheit_to_celcius, temp_fahrenheit))
print(f"Temperatures converted using map: {temp_celcius}")

请注意我们如何消除空列表的 for 循环和初始化,并将它们替换为对 map函数的单个调用。

我们使用list方法将返回的地图对象转换为列表。
如果我们希望我们的结果是一个元组,我们可以等效地使用 tuple()方法。

日期:2020-07-15 11:16:24 来源:oir作者:oir