
本文探讨了如何在pandas dataframe中,根据一个series提供行索引和列标签的映射关系,高效、向量化地选取特定元素。通过介绍`factorize`结合`reindex`和`merge`两种主要方法,详细阐述了如何避免低效的循环操作,实现性能优化,并提供了具体的代码示例和注意事项。
在数据分析和处理中,我们经常需要从Pandas DataFrame中提取特定位置的元素。一个常见的场景是,我们有一个DataFrame,以及一个Series。这个Series的索引对应DataFrame的列名,而Series的值则对应DataFrame的行索引。我们的目标是根据这种映射关系,高效地提取DataFrame中对应位置的元素,并返回一个Series或列表。
传统的做法可能会采用迭代Series的方式,逐个查找并赋值,例如:
import pandas as pd # 假设 df 和 sr 已定义 # result = pd.Series() # for c, i in sr.items(): # result[c] = df.loc[i, c]
这种方法虽然直观,但对于大型数据集而言,其性能瓶颈在于循环操作,无法充分利用Pandas和NumPy的向量化优势,导致效率低下。本文将介绍两种更高效、向量化的解决方案。
为了更好地说明问题和解决方案,我们首先定义一个示例DataFrame和Series:
import pandas as pd
import numpy as np
# 示例 DataFrame
data = np.arange(25).reshape(5, 5)
df = pd.DataFrame(data, columns=list('abcde'))
print("DataFrame (df):\n", df)
# 示例 Series
sr = pd.Series({'a': 1, 'c': 2, 'b': 3})
print("\nSeries (sr):\n", sr)输出:
DataFrame (df):
a b c d e
0 0 1 2 3 4
1 5 6 7 8 9
2 10 11 12 13 14
3 15 16 17 18 19
4 20 21 22 23 24
Series (sr):
a 1
c 2
b 3
dtype: int64我们的目标是根据sr的映射关系:
这种方法的核心思想是将DataFrame的行索引和列标签以及Series中的对应值,都转换为整数位置编码。然后,通过reindex对DataFrame进行对齐,最后利用NumPy的二维数组索引能力进行高效查找。
编码Series的值和索引: 使用pd.factorize()将sr的值(行索引)和sr的索引(列标签)分别转换为整数编码及其对应的唯一标签列表。
a_i, idx = pd.factorize(sr) # a_i 是 sr 值的整数编码,idx 是 sr 值的唯一列表 a_c, col = pd.factorize(sr.index) # a_c 是 sr 索引的整数编码,col 是 sr 索引的唯一列表
对齐DataFrame: 使用df.reindex(index=idx, columns=col)根据sr中涉及到的行索引和列标签来重新排列DataFrame。这会创建一个新的DataFrame视图,其行索引和列名与idx和col完全匹配。
reindexed_df = df.reindex(index=idx, columns=col)
此时,reindexed_df的形状和内容已经准备好,可以与a_i和a_c的整数编码进行对应。
执行二维数组查找: 将reindexed_df转换为NumPy数组,然后利用a_i和a_c作为行和列的整数索引进行查找。
extracted_values = reindexed_df.to_numpy()[a_i, a_c]
[a_i, a_c] 构成了 (行索引数组, 列索引数组) 的形式,NumPy会根据这些对应位置提取元素。
构建结果Series: 将提取到的值extracted_values与原始sr的索引重新组合,形成最终的Series。
out = pd.Series(extracted_values, index=sr.index)
# 方法一:利用 factorize 和 reindex
a_i, idx = pd.factorize(sr)
a_c, col = pd.factorize(sr.index)
out_factorize = pd.Series(df.reindex(index=idx, columns=col).to_numpy()[a_i, a_c],
index=sr.index)
print("\n方法一结果 (factorize):\n", out_factorize)输出:
方法一结果 (factorize): a 5 c 12 b 16 dtype: int64
另一种方法是利用Pandas的merge操作。这种方法通常在数据转换和重塑时非常有用,但可能比factorize方法稍微复杂一些。
重塑 sr: 将sr转换为一个DataFrame,使其索引成为一个常规列,方便后续合并。
sr_df = sr.reset_index() # 结果是 DataFrame: 'index' | 0
# 'a' | 1
# 'c' | 2
# 'b' | 3重塑 df: 将df堆叠(stack)成一个Series,其索引将是多级索引 (行索引, 列标签)。然后给这个Series命名,方便后续合并。
df_stacked = df.stack().rename('out')
# df_stacked 的索引格式为 (行索引, 列标签)
# 例如:(0, 'a') -> 0, (0, 'b') -> 1, ...执行合并操作: 将sr_df与df_stacked进行合并。
merged_df = sr_df.merge(df_stacked,
left_on=[0, 'index'],
right_index=True,
how='left')整理结果: 合并后的DataFrame包含原始sr的信息以及从df中提取的值。我们需要将其整理回原始sr的索引和结构。
out_merge = merged_df.set_index('index')['out']# 方法二:利用 merge
out_merge = (sr.reset_index()
.merge(df.stack().rename('out'),
left_on=[0, 'index'],
right_index=True,
how='left')
.set_index('index')['out']
)
print("\n方法二结果 (merge):\n", out_merge)输出:
方法二结果 (merge): index a 5 c 12 b 16 Name: out, dtype: int64
请注意,merge方法的结果Series的name可能会有所不同,但内容是相同的。
sr 索引重复的处理: 上述两种向量化方法在处理sr的索引存在重复时,与原始的迭代循环行为可能不同。如果sr的索引有重复,例如 sr = pd.Series({'a': 1, 'c': 2, 'b': 3, 'a': 4}),原始循环会取最后一个'a'对应的值(df.loc[4, 'a'])。而factorize方法会根据sr的顺序处理,merge方法则可能返回多个匹配项。 如果需要模拟原始循环“取最后一个”的行为,应在执行向量化操作前对sr进行预处理,例如:
# 如果 sr 的索引可能重复,且希望保留最后一个匹配项 sr_cleaned = sr[~sr.index.duplicated(keep='last')] # 然后将 sr_cleaned 代替 sr 用于上述方法
这会确保每个唯一的sr索引只对应一个值。
性能考量: 通常情况下,factorize结合reindex和NumPy索引的方法在性能上会优于merge方法,尤其是在处理大规模数据时,因为它更直接地利用了底层数组操作。merge方法涉及更多的数据重塑和哈希表查找,开销相对较大。
本文介绍了两种在Pandas DataFrame中高效、向量化地选取特定元素的方法,以替代低效的循环迭代。factorize结合reindex和NumPy二维索引的方法,通过将标签转换为整数位置,实现了极高的查找效率。而merge方法则通过数据重塑和融合,提供了一种更具通用性的解决方案。在实际应用中,根据数据规模和对性能的要求,可以选择最适合的方法。同时,对于sr中可能存在的索引重复问题,也提供了相应的预处理建议,以确保结果的准确性。掌握这些向量化技巧,对于提升Pandas数据处理的效率至关重要。
以上就是高效选取Pandas DataFrame特定元素的向量化方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号