
10 个 Python 单行代码,将提升您的数据准备工作流程
图片来源:编辑 | Midjourney
数据准备是数据项目生命周期中的一个步骤,在此步骤中,我们为后续流程(如数据分析和机器学习建模)准备原始数据。数据准备可以说直接决定了您数据项目的成败,因为准备不足将导致糟糕的结果。
鉴于数据准备的重要性,我们需要一种 proper 的方法。这就是为什么本文将探讨简单的单行 Python 代码如何优化您的数据准备工作流。
1. 使用 Pipe 链式转换
数据准备通常涉及多个连续的转换。例如,我们不仅必须过滤行、重命名列和排序数据,而且还必须按照该特定项目的确切顺序进行。
多个数据转换经常会使其变得混乱,因为需要以瀑布式的方式处理多个代码。
然而,Pandas 的
1 |
pipe() |
函数可以使转换链更清晰、更易读。这消除了对中间变量的需求,并允许管道以易于预定义的顺序处理自定义函数。
我们可以使用以下单行代码为数据准备链接多个函数。
1 |
df = df.pipe(lambda d: d.rename(columns={'old_name': 'new_name'})).pipe(lambda d: d.query('new_name > 10')) |
上面的代码显示了 pipe 如何通过多个自定义函数进行函数链接,并高效地执行数据转换。
2. 使用多个聚合进行数据透视
数据透视是重新排列数据以方便用户分析和理解的过程。透视通常通过将数据行转换为列,反之亦然,并对特定维度进行数据聚合来完成。
有时,数据准备在数据透视中涉及多个聚合,具体取决于分析需求。如果我们启动和访问中间变量来执行此操作,可能会变得混乱。
幸运的是,在 Pandas 中,一旦创建了预先存在的数据,就可以使用单行 Python 代码轻松执行多个聚合。让我们看一下。
假设您有以下包含多个列、维度和值的示例数据集。
1 2 3 4 5 6 7 8 9 |
import pandas as pd import numpy as np data = { 'category': ['A', 'A', 'B', 'B', 'C', 'C'], 'sub_category': ['X', 'Y', 'X', 'Y', 'X', 'Y'], 'value': [10, 20, 30, 40, 50, 60] } df = pd.DataFrame(data) |
然后,我们要准备包含聚合统计量的数据,例如每个类别的平均值和总和。为此,我们可以使用以下代码。
1 |
pivot_df = df.pivot_table(index='category', columns='sub_category', values='value', aggfunc={'value': [np.mean, np.sum]}) |
通过结合使用数据透视表和聚合函数,我们可以轻松准备数据,而无需执行多行代码。
3. 使用多个聚合进行时间序列重采样
多重聚合不仅适用于标准表格数据,也适用于时间序列数据,尤其是在数据重采样之后。时间序列数据重采样是一种以我们想要的时间频率(例如每日、每周、每月等)进行数据摘要的方法。
一旦我们重采样了数据,我们就应该对数据集进行多个聚合,以便为任何后续活动做好准备。要完成以上所有操作,我们可以使用以下单行代码。
1 |
df_resampled = df.set_index('timestamp').resample('D').agg({'value': ['mean', 'max'], 'count': 'sum'}).reset_index() |
只需替换列名,例如 value()
或 count(),替换为您想要的名称。您也可以选择所需的任何类型的聚合。
4. 条件选择用于赋值
处理原始数据时,我们通常需要创建新特征。例如,我们可能想将员工的薪水分为三个不同的值。
我们可以通过使用多个条件 if-else 语句或某种循环来实现。但是,通过使用 NumPy,也可以将其简化为单行代码。
让我们使用以下代码初始化一个员工样本数据集
1 2 3 4 5 6 7 8 9 |
import pandas as pd import numpy as np data = { 'employee_id': [1, 2, 3, 4, 5], 'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'], 'salary': [3500, 5000, 2500, 8000, 1000] } df = pd.DataFrame(data) |
然后,我们可以使用以下单行代码(为了可读性而分多行显示)将薪水分为三个档次。
1 2 3 4 5 6 7 8 |
df['salary_level'] = np.select( [ df['salary'] < 3000, (df['salary'] >= 3000) & (df['salary'] < 6000), df['salary'] >= 6000 ], ['Low', 'Medium', 'High'] ) |
结果将是一个新列,其中包含根据我们制定的条件确定的值。
5. 对多个列进行条件替换
有时我们不想过滤掉选定的数据;相反,我们想用适合我们工作的其他值替换它。
使用 NumPy,我们可以通过单行代码高效地替换多列中的值。
例如,这里有一个示例,说明我们如何组合使用 Pandas 的 apply()
方法和 Numpy 的 where()
函数来替换不同列中的值。
1 |
df[['col1','col2']] = df[['col1','col2']].apply(lambda col: np.where(col > 0, col, np.nan)) |
使用上面的代码,您可以轻松地用所需条件替换值。
6. 组合多个列
当我们处理数据时,有时我们会将多个列表示为一个特征,而不是将它们保留原样。
对于组合多个列,我们可以使用简单的统计量(如平均值、总和、标准差等)对其进行聚合。但是,还有其他方法可以将多个列组合为一个,例如通过字符串组合。
这不一定适用于数值数据,因为结果可能不是最佳的,但对于任何文本数据来说都足够了,尤其是当组合有意义时。
要做到这一点,我们可以使用以下代码
1 |
df['combined'] = df[['col1', 'col2', 'col3']].astype(str).agg('_'.join, axis=1) |
axis 等于 1 将按行连接,以“_”作为分隔符。您可以将其替换为空格、连字符或您认为合适的任何其他分隔符。
7. 列拆分
我们已经讨论了将列组合为一个,但有时将一个特征拆分为几个不同的特征更有益。
原理与上面相同,您可以使用 Python 单行代码轻松拆分。
1 |
df[['first', 'last']] = df['full_name'].str.split(' ', n=1, expand=True) |
上面的代码将分隔第一个空格,并将其拆分为多个列,即使文本中有多个空格。
您可以根据需要更改要拆分的分隔符。
8. 异常值识别和移除
异常值有时需要移除,因为它们会扭曲我们的分析和机器学习算法。不总是如此,但您可以在仔细分析后考虑移除它们。
异常值识别有许多方法,但最简单的方法是使用百分位数。
例如,我们将数据中的底部和顶部 5% 定义为异常值。为此,我们可以使用以下单行代码。
1 |
df['capped'] = df['value'].clip(lower=df['value'].quantile(0.05), upper=df['value'].quantile(0.95)) |
使用上面的代码,我们可以识别并截断数据集中不想要的异常值。
9. 使用 Reduce 合并多个 DataFrame
在我们的工作中,我们经常会遇到多个数据集,并且可能希望将它们合并。
在 Pandas 中,您可以使用 merge()
函数轻松完成此操作。但是,数据集越大,复杂性就越高。
如果是这种情况,我们可以使用 reduce()
函数,该函数允许我们在不手动嵌套多个 merge 函数调用的情况下合并 DataFrame 列表。
例如,我们有以下多个数据集,我们将其放入一个列表中。
1 2 3 4 5 6 7 8 |
import pandas as pd from functools import reduce df1 = pd.DataFrame({'key': [1, 2, 3], 'A': ['a1', 'a2', 'a3']}) df2 = pd.DataFrame({'key': [2, 3, 4], 'B': ['b2', 'b3', 'b4']}) df3 = pd.DataFrame({'key': [3, 4, 5], 'C': ['c3', 'c4', 'c5']}) list_of_dfs = [df1, df2, df3] |
使用 reduce()
,我们可以使用以下代码合并多个 DataFrame。
1 |
df_merged = reduce(lambda left, right: pd.merge(left, right, on='key', how='outer'), list_of_dfs) |
结果将取决于您如何合并 DataFrame 和合并的键,但这比拥有中间变量来合并多个数据集更容易。
10. 使用 Eval 优化 DataFrame 查询
基于 DataFrame 计算创建新列可能需要一些时间,特别是如果数据量很大。
为了优化此过程,我们可以使用 Pandas 的 eval()
函数。通过使用类似于 query()
函数的过程,eval()
可以提高执行时间,同时减少对中间对象的需求。
例如,我们可以使用以下代码进行操作。
1 |
df = df.eval("col3 = (col1 * 0.8 + col2 * 0.2) / col4", inplace=False) |
使用上面的代码,借助 eval()
,我们可以更快地创建列,并且可读性更高。
结论
在本文中,我们介绍了一些可以真正加快和简化您的数据准备工作流的 Python 单行代码。从使用 pipe()
链接转换到使用 eval()
优化 DataFrame 查询,这些技巧都旨在让您在处理数据时生活更轻松。无论您是透视表、重采样时间序列还是合并多个数据集,这些单行代码都可以帮助您快速、干净地完成工作。
希望这对您有所帮助!
感谢您的文章。单行代码非常强大,但它们可能会变得过于复杂且难以阅读。在合并具有相同列名的 DataFrame 时(如您的示例中),pd.concat() 比 reduce 更快。
不客气,Dan!
#4 充满语法错误
免费吗?
你好,感谢您花时间列出这些很棒的技术。我确实觉得如果能有示例输出会对我更好地理解正在发生的事情有所帮助。也许可以减少到五个单行代码,每个代码包含更多信息。但目前,我会将它们复制粘贴到 ChatGPT 中查看一些示例🙂
谢谢!
不客气!请及时向我们汇报您的进展!
避免第 02 点中的警告,替换为“… aggfunc={‘value’: [“mean”, “sum”]})”
为了可读性并允许直接复制到 Jupyter(这是大多数人对这类教程的做法),请在中间和最后包含“df.head(5)”等内容。第 2 步中的最后一条命令应为:“pivot_df.head(5)”,例如。
否则,这是一篇信息丰富的帖子 - 只是有太多小瑕疵(也请参见 (4) - Tom 的帖子)
有没有办法让这篇文章的广告不那么烦人?我很喜欢内容。但不喜欢广告如此分散注意力。
你好 Russ……谢谢你的反馈!
以下几行
“df[‘salary_level’] = np.select(
[df[‘salary’] = 3000] & (df[‘salary’] 6000),
[‘Low’, ‘Medium’, ‘High’])”
出现错误
Cell In[29], line 2
[df[‘salary’] = 3000] & (df[‘salary’] 6000),
^
SyntaxError: cannot assign to subscript here. Maybe you meant ‘==’ instead of ‘=’?
我哪里错了?
Phlip