性能至上。这不仅仅适用于编程或数据科学。当你处理更大的数据集时,这个简易实现能为你节省数小时的时间。
在NumPy中,如果你追求数值计算的速度,通用函数(ufuncs)将成为你的首选工具。
因此,本文将介绍并解析如何使用ufuncs,以及它们如何高效地将真实数据转化为洞见。正如往常一样,我们将使用平台上的真实数据集进行演示。让我们先来探索一下这个数据集。
价格预测:NumPy ufuncs实战项目
这里是这个数据项目的链接:https://platform.stratascratch.com/data-projects/predicting-price
在这个数据项目中,Haensel AMS要求我们作为数据科学职位的招聘流程中的家庭作业来完成。我们先读取这个数据集。
import pandas as pd
df = pd.read_csv("sample.csv")
df.head()
输出如下:
(NumPy ufuncs实战项目示例输出)
现在让我们看看数据的列。代码如下:
df.info()
输出如下:
(NumPy ufuncs实战项目示例输出)
理解NumPy的通用函数(ufuncs)
让我们一步一步来。
NumPy拥有通用函数,简称ufuncs。
这些函数可以对数组中的元素进行操作。
你无需编写循环,只需调用函数,NumPy会自动将其应用到所有元素上。
其底层使用C语言代码,因此执行时几乎没有额外开销。
让我们从一个简单的操作开始。
步骤1:逐元素相加
你有两列——para2和para3。你想要它们逐行相加。代码如下:
import numpy as np
sum_array = np.add(df["para2"], df["para3"])
print(sum_array[:5])
输出如下:
(NumPy ufuncs实战项目示例输出)
sum_array中的每个值都是对应行para2和para3的和。整个操作一气呵成,覆盖所有行。
步骤2:逐元素相乘
你现在有了sum_array。
假设你想对这个数组进行缩放,比如应用一个比率或乘数。
这时候就可以用np.multiply。
它会对数组中的每个元素分配一个数字。代码如下:
scaled_sum = np.multiply(sum_array, 1.1)
print(scaled_sum[:5])
输出如下:
(NumPy ufuncs实战项目示例输出)
现在,每个值都提升了10%。
你无需遍历任何内容——NumPy已经帮你处理好了。所有算术操作都是如此。你可以传入数组,也可以传入标量,NumPy都会逐元素处理。
步骤3:逐元素比较
假如你想找出para3大于某个值的行。
不必手动一行行检查,直接用比较函数。
np.greater会判断数组中的每个元素是否大于另一个值或数组。
它会返回一个布尔数组——满足条件为True,否则为False。
mask = np.greater(df["para3"], 2500)
print(mask[:5])
输出如下:
(NumPy ufuncs实战项目示例输出)
你可以用这个掩码来筛选原始DataFrame:
filtered = df[mask]
print(filtered.head())
输出如下:
(NumPy ufuncs实战项目示例输出)
现在,你已经创建了条件并直接应用到数据集上。不需要if语句或循环。如果想深入探究,可以看看Python中的数组切片如何实现这一切。
步骤4:逻辑操作
你已经用一个条件筛选过数据。 现在假设你想要para3 > 2500 且 para1 < 500的行。 你可以用逻辑函数组合布尔数组。 np.logical_and可以直接实现。代码如下:
condition = np.logical_and(df["para3"] > 2500, df["para1"] < 500)
filtered_rows = df[condition]
print(filtered_rows.head())
输出如下:
(NumPy ufuncs实战项目示例输出)
这样你就得到了同时满足两个条件的行。 无需嵌套循环,也不需要.apply(),只需一行代码。
逻辑操作还包括np.logical_or、np.logical_not和np.logical_xor。 这些操作都以同样的方式处理数组——逐元素进行。
步骤5:聚合ufuncs,如np.sum、np.mean
你已经完成了逐元素操作。 现在,我们来看一些可以将数组归约为单一值的函数。
np.sum、np.mean和np.max就是其中的几个例子。 它们分别计算某个轴上的总和、平均值或最大值。 代码如下:
total_para2 = np.sum(df["para2"])
mean_para3 = np.mean(df["para3"])
max_para1 = np.max(df["para1"])
print(total_para2, mean_para3, max_para1)
输出如下:
(NumPy ufuncs实战项目示例输出)
每个函数都返回一个标量值。 无需循环,无需逐列遍历——每个计算只需一行代码。 你还可以通过设置axis=1来按行聚合。
row_sums = np.sum(df[["para1", "para2"]], axis=1)
print(row_sums[:5])
输出如下:
(NumPy ufuncs实战项目示例输出)
聚合可以将数据集浓缩为有用的洞察。现在你已经拥有了总和、均值或极值,可以随时使用。
步骤6:广播(Broadcasting)
有时数组的形状并不相同,但你仍然希望在它们之间进行操作。
广播允许你在不改变数组形状的情况下实现这一点。NumPy会自动将较小的数组扩展到目标维度。代码如下:
centered_para2 = df["para2"] - np.mean(df["para2"])
print(centered_para2[:5])
输出如下:
(NumPy ufuncs实战项目示例输出)
现在,每一行都显示了该值与该列平均值的差值。均值是一个标量,但NumPy自动将其扩展到整列。
广播可以作用于数组、标量和多维数组。不需要额外的代码,NumPy会自动处理对齐规则。
你可以进一步探索NumPy在数据科学面试中的应用。
NumPy ufuncs的性能提升
你用循环完成了所有操作——从两列相加到按条件筛选行。
但循环在大数据集下表现缓慢。
通用函数避免了这一瓶颈。
它们直接在内存级别的结构上运行,避免了Python循环的额外负担。
当你将para2和para3相加时,NumPy在后台一次性完成了整个操作。
而循环则需要一行一行地处理并存储新值,重复进行。
这就是为什么你在检查para3是否大于2500时立即获得了结果;ufuncs总是能立刻返回结果。
而在循环中,同样的检查需要逐行进行,既慢又消耗资源。
即使是简单的缩放或减去均值,ufuncs的优势也非常明显。
它们能为你完成这些操作,而不会增加代码量或降低工作流效率。
随着数据量的增长,这种性能优势会更加明显。
它们比用Python for循环处理大规模数据(数千甚至数百万行)要快得多、节省内存,让你能轻松应对庞大的数据处理需求。
结语
在本文中,我们发现了在数组中进行操作的新方法。这些特性将大大节省你的资源,尤其是在你需要处理大型数据集时——而这通常正是你的工作内容。