在我上一篇文章中,我讨论了 SQL 用户自定义函数。但与 SQL 相比,Python 在函数设计方面的多功能性脱颖而出。根据我在科技公司工作的经验,很明显,没有广泛使用 Python 函数,任何数据科学项目都无法完成。Python 已经成为数据科学家高效管理和分析数据、处理复杂任务和部署产品功能的基本工具。以其核心的广泛函数范围为特点,Python 在数据科学领域证明了自己是一个强大的工具。然而,由于有这么多类型的函数可用,数据科学家熟悉所有这些函数既困难又不可能。今天的文章将涵盖在现实世界数据科学中常用的前 8 种函数类型,解释它们背后的复杂逻辑和机制,这些内容在其他教程中很少提及。本文还将澄清不同类型函数之间的混淆,这些函数经常被误认为是彼此。最后,一个迷你项目将演示如何有效地在实际中应用这些函数中的几个。
Python 前 8 种常用函数类型
1. 基本函数
内置函数是 Python 中预定义的,执行诸如数据结构创建、类型转换和数学计算等常见操作。最常用的内置函数是用于数学运算、类型转换、序列/迭代器、输入/输出和对象内省的函数。
1.1 数学函数
数学函数执行数学运算。
import math
numbers = [1, 2, 3, 4, 5]
total = sum(numbers)
average = sum(numbers) / len(numbers)
maximum = max(numbers)
minimum = min(numbers)
absolute = abs(-10)
rounded = round(3.14159, 2)
square_root = math.sqrt(16)
1.2 类型转换函数
类型转换函数在不同的数据类型之间进行转换。
number_string = "123"
float_number = "3.14"
integer = int(number_string)
float_value = float(float_number)
string_value = str(integer)
boolean_value = bool(integer)
1.3 序列/迭代器函数
序列/迭代器函数与序列和迭代器一起工作。
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
enumerated = list(enumerate(numbers))
zipped = list(zip(numbers, squared))
print(f"Squared: {squared}")
print(f"Evens: {evens}")
print(f"Enumerated: {enumerated}")
print(f"Zipped: {zipped}")
1.4 输入/输出函数
输入/输出函数处理输入和输出操作。
name = input("Enter your name: ")
print(f"Hello, {name}!")
with open("example.txt", "w") as f:
print("This is a line of text.", file=f)
with open("example.txt", "r") as f:
content = f.read()
print(f"File content: {content}")
1.5 对象内省函数
对象内省函数在运行时检查对象属性。
class ExampleClass:
def example_method(self):
pass
obj = ExampleClass()
print(f"Type of obj: {type(obj)}")
print(f"Attributes of obj: {dir(obj)}")
print(f"Is obj an instance of ExampleClass? {isinstance(obj, ExampleClass)}")
print(f"Does obj have example_method? {hasattr(obj, 'example_method')}")
内置函数效率高,因为它们随时可用,并且在 Python 代码中标准化。新用户可能会将导入的函数与内置函数混淆,因为两者都是预定义的,并且具有类似的作用。然而,导入的函数,或称为库函数,是外部库(如 Numpy、Pandas 或 Scikit-learn)的一部分。这些函数是数据科学和机器学习工作流程中的基本工具,但它们只能在导入这些外部库之后使用。
2. 用户自定义函数
用户定义函数(UDFs)是由开发者创建的用于实现特定逻辑/操作、处理复杂任务或在不同项目部分重用代码的自定义函数。
Python UDFs 为开发者带来 5 大好处:
代码清晰度:在将重复或复杂的代码封装在函数中之后,脚本可以变得更加简洁和易于阅读。
调试:UDF 可以单独测试和调试。这一特性使得在数据处理管道中识别和修复错误变得更加容易。
可扩展性:精心设计的 UDF 有助于开发者扩展代码库,并在项目增长时使其更易于管理。
协作:模块化函数允许团队成员一起工作,每个人专注于一个大项目的一个不同部分。
优化:模块化函数通过针对独立特定函数进行优化,而不是彻底重写整个代码库,从而改变了模型升级和迭代的游戏规则。
这里有一些简单的 UDF 示例:
def add(a, b):
return a + b
def various_types(x):
if x < 0:
return "Hello!"
else:
return 0
print(various_types(1))
print(various_types(-1))
从第二个例子中可以看出,与像 SQL 这样的其他编程语言不同,Python 不需要开发者显式声明函数的返回类型。Python 函数可以通过return关键字返回任何类型的值。
在更广泛的意义上,一些函数,如闭包函数,也可以被归类为 UDF(用户定义函数)。然而,由于它们的独特结构和用例,我将在稍后单独介绍它们。
3. Lambda 函数
Lambda 函数是通过lambda关键字定义的小型匿名函数。它们不需要开发者编写return关键字,因为冒号后面的表达式会自动返回。
Lambda 函数非常适合简短、简洁的操作,便于定义。它们经常与sorted、filter和map等函数一起使用。
data = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, data))
print(f"Squared: {squared}")
4. 高阶函数
高阶函数将其他函数作为参数或返回函数。它们可以实现函数组合、回调和装饰器,从而增强代码的灵活性和可重用性。
这里有一个高阶函数的例子:
def apply_function(func, value):
return func(value)
def square(x):
return x * x
result = apply_function(square, 5) # apply_function takes the 'square' function as an argument
print(result) # Output: 25
高阶函数与嵌套函数看起来相似,容易混淆。但它们之间存在关键差异。高阶函数是通过其接受或返回函数的能力来定义的,而嵌套函数是通过其在另一个函数中的位置来定义的。
我将通过嵌套函数的例子来解释这两种函数之间的区别:
def outer_function(a, b):
def inner_function(c):
return c * 2
return inner_function(a) + inner_function(b)
result = outer_function(3, 5)
print(result) # Output will be 16
在这个例子中,inner_function是一个嵌套函数,因为它是在outer_function内部定义的。而在高阶函数的例子中,apply_function是一个高阶函数,因为它将另一个函数(square)作为参数。尽管存在差异,但高阶函数可以包含嵌套函数。
5. 生成器函数
生成器函数使用yield关键字来返回一个迭代器。它们不是返回单个值并退出,而是产生一个值,暂停其执行,并在请求下一个值时从上次停止的地方继续。这允许一次生成一个值,而不是一次性生成所有值。
生成器函数可以按如下方式生成斐波那契数列:
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
fib_seq = list(fibonacci(10))
print(f"Fibonacci sequence: {fib_seq}")
通过使用return和for loop函数,可以达到相同的结果。但这样的函数会立即终止并返回单个值。你必须牺牲效率,将整个序列存储在内存中。
此后,生成器函数在内存效率方面非常出色,尤其是在生成大型序列时。由于性能的显著提高和适合管道处理,生成器函数可以用于数据流和批量处理。此外,生成器常用于在图像处理和深度学习中应用随机变换。由于它们的懒加载数据特性,生成器在处理大型数据集或预处理计算成本高昂时非常有用。
6. 递归函数
递归函数在其定义中调用自身以解决问题。这类函数通常用于具有自然递归结构的算法。例如,它可以计算阶乘。
def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n-1)
阶乘函数也可以使用 lambda 表达式实现:
factorial = lambda n: 1 if n == 0 else n*factorial(n-1)
7. 装饰器函数
装饰器函数在不更改其源代码的情况下修改或增强其他函数的行为。装饰器有效地提高了代码的可重用性,促进了关注点的分离,允许以模块化和可组合的方式构建复杂行为,并允许在不直接访问源代码的情况下进行修改。因此,装饰器常用于日志记录、计时/分析、访问控制/授权、记忆/缓存、验证和重试逻辑。
以下脚本演示了如何使用装饰器函数来测量函数的执行时间,这对于优化代码非常有帮助。
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.2f} seconds to execute")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
slow_function()
脚本打印出slow_function()的执行时间为:“slow_function()执行耗时 2.01 秒”。
8. 闭包函数
闭包函数是在外部函数内部定义的函数,即使在外部函数执行完成后,它也能访问外部函数的变量。闭包函数用于创建函数工厂、回调和事件处理程序、装饰器和有状态对象。以下是一个创建乘法函数的闭包函数示例:
def multiplier(factor):
def multiply_by(x):
return x * factor
return multiply_by
# Create a function that multiplies by 3
times_three = multiplier(3)
# Create a function that multiplies by 5
times_five = multiplier(5)
# Use the closure functions
print(times_three(10)) # Output: 30
print(times_five(10)) # Output: 50
新用户经常将闭包函数与嵌套函数混淆,因为两者都涉及在另一个函数内部定义函数。在这两种类型中,内部函数都可以访问外部函数的变量和参数。但关键区别在于,闭包函数的内部函数即使在外部函数执行完成后也会记住其封装环境的状态,而嵌套函数在外部函数执行完成后不会保留外部函数变量的状态。这种状态保留的差异决定了这两种函数类型的独特用法。
尽管闭包函数和递归函数在它们的底层逻辑上有些相似之处——例如作为参数传递、由其他函数返回以及分配给变量,但递归函数通过重复减少问题规模直到满足基例来操作,而不是像闭包函数那样保留状态。相比之下,闭包函数不涉及重复的自我调用。
一个小型机器学习项目
在这个演示中,我将使用来自 UCI 机器学习仓库的葡萄酒质量数据集。您可以访问信息页面这里[here]或在此处直接下载数据集。
本数据集根据 Creative Commons Attribution 4.0 International (CC BY 4.0)许可协议授权,允许用于任何目的的共享和改编,前提是必须给予适当的信用。
葡萄酒质量数据集包括 11 种葡萄酒的化学特性以及介于 0(非常差)和 10(优秀)之间的质量评分。鉴于这是一个回归问题,我将使用随机森林回归器来训练一个预测葡萄酒质量的模型。
我们将首先导入所有必要的库。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, r2_score
import requests
from io import StringIO
接下来,我们将创建三个Python 用户定义函数。第一个函数将下载并加载葡萄酒质量数据集。第二个函数将计算所有变量的基本统计信息。最后一个函数将从现有特征中创建 2 个新特征。
# User-defined functions
def load_wine_data(url):
"""Download and load wine data from a given URL."""
response = requests.get(url)
data = StringIO(response.text)
df = pd.read_csv(data, sep=';')
return df
def calculate_stats(df, columns):
"""Calculate basic statistics for specified columns."""
stats = {}
for col in columns:
stats[col] = {
'mean': np.mean(df[col]),
'median': np.median(df[col]),
'std': np.std(df[col])
}
return stats
def engineer_features(df):
"""Create new features from existing ones."""
df['total_acidity'] = df['fixed acidity'] + df['volatile acidity']
df['sugar_to_acid_ratio'] = df['residual sugar'] / df['total_acidity']
return df
然后,我们将通过应用转换创建一个高阶函数。
# Higher-order function for applying transformations
def apply_transformations(df, transformations):
for column, transform_func in transformations.items():
df[column] = df[column].apply(transform_func)
return df
最后,我们将实现主要的工作流程,该流程包含所有函数(包括用户定义函数和高阶函数),以及模型训练和预测过程。
由于高阶函数可以接受其他函数作为参数或返回函数,在这个例子中,函数apply_transformation接受另一个函数transform_func作为参数,该函数通过 lambda 表达式定义。
# Main workflow
def main():
# Load data
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
df = load_wine_data(url)
# Data cleaning (using built-in function)
df = df.dropna()
# Feature engineering
df = engineer_features(df)
# Apply transformations using lambda functions
transformations = {
'alcohol': lambda x: x / 100, # Convert to decimal
'pH': lambda x: 10**(-x) # Convert pH to hydrogen ion concentration
}
df = apply_transformations(df, transformations)
# Calculate statistics for numeric columns
numeric_columns = df.select_dtypes(include=[np.number]).columns
stats = calculate_stats(df, numeric_columns)
print("Basic statistics:")
print(pd.DataFrame(stats))
# Prepare features and target
X = df.drop('quality', axis=1)
y = df['quality']
# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Scale features
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Train model
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)
# Make predictions
y_pred = model.predict(X_test_scaled)
# Calculate metrics
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"nMean Squared Error: {mse:.4f}")
print(f"R-squared Score: {r2:.4f}")
# Print feature importances (using lambda function)
feature_importances = sorted(
zip(model.feature_importances_, X.columns),
key=lambda x: x[0],
reverse=True
)
print("nTop 5 Feature Importances:")
for importance, feature in feature_importances[:5]:
print(f"{feature}: {importance:.4f}")
# Call the main workflow
if __name__ == "__main__":
main()
最终输出是:
图片由作者提供
结论
Python 丰富的函数功能并不仅仅是为了完成数据科学项目——它还在于以优雅且高效的方式完成这些项目。从我的 Python 工作经验来看,我深知尝试掌握所有这些函数可能会多么令人感到压倒。但我也亲身体验到,它们对我们工作是多么有价值且关键。这就是为什么值得花时间通过持续练习来掌握这些函数。
40

被折叠的 条评论
为什么被折叠?



