欢迎光临
我们一直在努力

Python NumPy - 数组的堆叠 hstack 与 vstack 函数的使用

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕NumPy这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • Python NumPy – 数组的堆叠 hstack 与 vstack 函数的使用 📊
    • 什么是数组堆叠? 🤔
      • 堆叠的基本概念
    • 深入理解 hstack 函数 🔍
      • 基本语法和参数
      • 一维数组的水平堆叠
      • 二维数组的水平堆叠
      • 不同形状数组的水平堆叠
    • 探索 vstack 函数 💡
      • 基本用法
      • 一维数组的垂直堆叠
      • 多维数组的垂直堆叠
    • 实际应用场景 🚀
      • 数据预处理中的应用
      • 图像处理中的应用
      • 时间序列数据处理
    • 高级技巧和最佳实践 🎯
      • 性能优化技巧
      • 错误处理和调试
      • 内存管理考虑
    • 与其他堆叠函数的比较 ⚖️
      • stack vs hstack/vstack
      • concatenate 函数的灵活性
    • 实战案例分析 📈
      • 案例1: 构建机器学习特征矩阵
      • 案例2: 数据聚合和统计分析
    • 性能基准测试 📊
    • 常见问题和解决方案 ❓
      • 问题1: 维度不匹配
      • 问题2: 内存不足
    • 最佳实践总结 ✅
      • 设计模式和架构建议
      • 性能优化指南
    • 相关资源推荐 📚
    • 总结 🎉
      • 关键要点回顾:
      • 学习建议:

Python NumPy – 数组的堆叠 hstack 与 vstack 函数的使用 📊

在数据科学和机器学习的世界中,NumPy 是一个不可或缺的强大工具包。它提供了高效的数组操作功能,使得处理大规模数值数据变得轻而易举。其中,hstack 和 vstack 函数是两个非常实用的数组堆叠方法,它们可以帮助我们将多个数组按水平或垂直方向进行组合。

什么是数组堆叠? 🤔

数组堆叠是指将两个或多个数组按照特定的方向连接在一起的操作。想象一下你在拼接乐高积木块,每一块都有自己的形状和大小,通过特定的方式将它们连接起来形成更大的结构。在 NumPy 中,我们可以通过堆叠来创建更复杂的多维数组结构。

堆叠的基本概念

堆叠操作的核心思想是:

  • 水平堆叠 (hstack):沿着数组的列方向(axis=1)进行连接
  • 垂直堆叠 (vstack):沿着数组的行方向(axis=0)进行连接

import numpy as np

# 创建示例数组
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])

print("原始数组 arr1:")
print(arr1)
print("\\n原始数组 arr2:")
print(arr2)

# 水平堆叠
h_result = np.hstack((arr1, arr2))
print("\\n水平堆叠结果:")
print(h_result)

# 垂直堆叠
v_result = np.vstack((arr1, arr2))
print("\\n垂直堆叠结果:")
print(v_result)

让我们通过一个简单的流程图来理解这个过程:

#mermaid-svg-GzVbLfDY8yl4TPxf{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GzVbLfDY8yl4TPxf .error-icon{fill:#552222;}#mermaid-svg-GzVbLfDY8yl4TPxf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GzVbLfDY8yl4TPxf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GzVbLfDY8yl4TPxf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GzVbLfDY8yl4TPxf .marker.cross{stroke:#333333;}#mermaid-svg-GzVbLfDY8yl4TPxf svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GzVbLfDY8yl4TPxf p{margin:0;}#mermaid-svg-GzVbLfDY8yl4TPxf .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf .cluster-label text{fill:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf .cluster-label span{color:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf .cluster-label span p{background-color:transparent;}#mermaid-svg-GzVbLfDY8yl4TPxf .label text,#mermaid-svg-GzVbLfDY8yl4TPxf span{fill:#333;color:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf .node rect,#mermaid-svg-GzVbLfDY8yl4TPxf .node circle,#mermaid-svg-GzVbLfDY8yl4TPxf .node ellipse,#mermaid-svg-GzVbLfDY8yl4TPxf .node polygon,#mermaid-svg-GzVbLfDY8yl4TPxf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GzVbLfDY8yl4TPxf .rough-node .label text,#mermaid-svg-GzVbLfDY8yl4TPxf .node .label text,#mermaid-svg-GzVbLfDY8yl4TPxf .image-shape .label,#mermaid-svg-GzVbLfDY8yl4TPxf .icon-shape .label{text-anchor:middle;}#mermaid-svg-GzVbLfDY8yl4TPxf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GzVbLfDY8yl4TPxf .rough-node .label,#mermaid-svg-GzVbLfDY8yl4TPxf .node .label,#mermaid-svg-GzVbLfDY8yl4TPxf .image-shape .label,#mermaid-svg-GzVbLfDY8yl4TPxf .icon-shape .label{text-align:center;}#mermaid-svg-GzVbLfDY8yl4TPxf .node.clickable{cursor:pointer;}#mermaid-svg-GzVbLfDY8yl4TPxf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GzVbLfDY8yl4TPxf .arrowheadPath{fill:#333333;}#mermaid-svg-GzVbLfDY8yl4TPxf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GzVbLfDY8yl4TPxf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GzVbLfDY8yl4TPxf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GzVbLfDY8yl4TPxf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GzVbLfDY8yl4TPxf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GzVbLfDY8yl4TPxf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GzVbLfDY8yl4TPxf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GzVbLfDY8yl4TPxf .cluster text{fill:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf .cluster span{color:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GzVbLfDY8yl4TPxf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GzVbLfDY8yl4TPxf rect.text{fill:none;stroke-width:0;}#mermaid-svg-GzVbLfDY8yl4TPxf .icon-shape,#mermaid-svg-GzVbLfDY8yl4TPxf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GzVbLfDY8yl4TPxf .icon-shape p,#mermaid-svg-GzVbLfDY8yl4TPxf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GzVbLfDY8yl4TPxf .icon-shape .label rect,#mermaid-svg-GzVbLfDY8yl4TPxf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GzVbLfDY8yl4TPxf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GzVbLfDY8yl4TPxf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GzVbLfDY8yl4TPxf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

水平堆叠

垂直堆叠

输入数组1

堆叠类型

输入数组2

按列连接

按行连接

输出数组

深入理解 hstack 函数 🔍

np.hstack() 函数用于水平堆叠数组,也就是沿着数组的第二个轴(列方向)进行连接。这个函数特别适用于需要将数据按特征维度扩展的情况。

基本语法和参数

numpy.hstack(tup)

参数说明:

  • tup: 包含数组的元组或列表,这些数组将被水平堆叠

返回值:

  • 返回一个新的数组,包含所有输入数组的水平堆叠结果

一维数组的水平堆叠

对于一维数组,hstack 相当于将数组简单地连接在一起:

import numpy as np

# 一维数组示例
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([7, 8, 9])

print("一维数组 a:", a)
print("一维数组 b:", b)
print("一维数组 c:", c)

# 水平堆叠
result = np.hstack((a, b, c))
print("\\n水平堆叠结果:", result)
print("结果形状:", result.shape)

二维数组的水平堆叠

对于二维数组,水平堆叠会增加列的数量:

import numpy as np

# 创建二维数组
matrix1 = np.array([[1, 2],
[3, 4],
[5, 6]])

matrix2 = np.array([[7, 8],
[9, 10],
[11, 12]])

print("矩阵1:")
print(matrix1)
print("形状:", matrix1.shape)

print("\\n矩阵2:")
print(matrix2)
print("形状:", matrix2.shape)

# 水平堆叠
h_stacked = np.hstack((matrix1, matrix2))
print("\\n水平堆叠结果:")
print(h_stacked)
print("结果形状:", h_stacked.shape)

不同形状数组的水平堆叠

需要注意的是,进行水平堆叠时,除了最后一个维度外,其他维度必须匹配:

import numpy as np

# 形状兼容的数组
arr1 = np.array([[1, 2, 3],
[4, 5, 6]])

arr2 = np.array([[7],
[8]])

print("数组1形状:", arr1.shape)
print("数组2形状:", arr2.shape)

# 这种情况是可以水平堆叠的
try:
result = np.hstack((arr1, arr2))
print("水平堆叠成功:")
print(result)
print("结果形状:", result.shape)
except ValueError as e:
print("错误:", e)

# 形状不兼容的例子
arr3 = np.array([[1, 2],
[3, 4]])

arr4 = np.array([[5, 6, 7]]) # 行数不匹配

print("\\n尝试堆叠形状不匹配的数组…")
try:
result = np.hstack((arr3, arr4))
except ValueError as e:
print("错误:", e)

探索 vstack 函数 💡

np.vstack() 函数用于垂直堆叠数组,即沿着数组的第一个轴(行方向)进行连接。这在需要增加样本数量或合并不同数据集时非常有用。

基本用法

import numpy as np

# 创建示例数组
top_array = np.array([[1, 2, 3],
[4, 5, 6]])

bottom_array = np.array([[7, 8, 9],
[10, 11, 12]])

print("顶部数组:")
print(top_array)
print("形状:", top_array.shape)

print("\\n底部数组:")
print(bottom_array)
print("形状:", bottom_array.shape)

# 垂直堆叠
v_stacked = np.vstack((top_array, bottom_array))
print("\\n垂直堆叠结果:")
print(v_stacked)
print("结果形状:", v_stacked.shape)

一维数组的垂直堆叠

对于一维数组,vstack 会将其转换为二维数组并按行堆叠:

import numpy as np

# 一维数组
row1 = np.array([1, 2, 3, 4])
row2 = np.array([5, 6, 7, 8])
row3 = np.array([9, 10, 11, 12])

print("行1:", row1)
print("行2:", row2)
print("行3:", row3)

# 垂直堆叠
v_result = np.vstack((row1, row2, row3))
print("\\n垂直堆叠结果:")
print(v_result)
print("结果形状:", v_result.shape)

多维数组的垂直堆叠

import numpy as np

# 三维数组示例
cube1 = np.array([[[1, 2], [3, 4]],
[[5, 6], [7, 8]]])

cube2 = np.array([[[9, 10], [11, 12]],
[[13, 14], [15, 16]]])

print("立方体1形状:", cube1.shape)
print("立方体2形状:", cube2.shape)

# 垂直堆叠(沿第一个轴)
v_3d = np.vstack((cube1, cube2))
print("垂直堆叠后形状:", v_3d.shape)

实际应用场景 🚀

了解了基本用法后,让我们看看这些函数在实际项目中的应用。

数据预处理中的应用

在机器学习项目中,经常需要将不同的特征数据集合并:

import numpy as np

# 模拟用户数据
user_ids = np.array([[1], [2], [3], [4], [5]])
ages = np.array([[25], [30], [35], [28], [42]])
incomes = np.array([[50000], [75000], [100000], [60000], [120000]])

print("用户ID:")
print(user_ids)
print("年龄:")
print(ages)
print("收入:")
print(incomes)

# 将特征水平堆叠成完整的特征矩阵
features = np.hstack((user_ids, ages, incomes))
print("\\n完整特征矩阵:")
print(features)
print("特征矩阵形状:", features.shape)

图像处理中的应用

在计算机视觉领域,图像通常表示为多维数组,堆叠操作常用于创建图像批次:

import numpy as np

# 模拟单个图像数据 (假设是 28×28 的灰度图像)
image1 = np.random.randint(0, 256, size=(28, 28))
image2 = np.random.randint(0, 256, size=(28, 28))
image3 = np.random.randint(0, 256, size=(28, 28))

print("单个图像形状:", image1.shape)

# 创建图像批次
batch = np.stack((image1, image2, image3)) # 使用 stack 而不是 vstack
print("图像批次形状:", batch.shape)

# 如果要使用 vstack,需要先增加维度
image1_expanded = np.expand_dims(image1, axis=0)
image2_expanded = np.expand_dims(image2, axis=0)
image3_expanded = np.expand_dims(image3, axis=0)

batch_vstack = np.vstack((image1_expanded, image2_expanded, image3_expanded))
print("使用 vstack 的批次形状:", batch_vstack.shape)

时间序列数据处理

在时间序列分析中,可能需要将不同时间段的数据垂直堆叠:

import numpy as np

# 模拟股票价格数据
january_prices = np.array([[100.5, 101.2, 99.8],
[101.0, 102.5, 100.1],
[102.3, 103.1, 101.9]])

february_prices = np.array([[103.5, 104.2, 102.8],
[104.0, 105.5, 103.1],
[105.3, 106.1, 104.9]])

march_prices = np.array([[106.5, 107.2, 105.8],
[107.0, 108.5, 106.1],
[108.3, 109.1, 107.9]])

print("一月价格数据形状:", january_prices.shape)
print("二月价格数据形状:", february_prices.shape)
print("三月价格数据形状:", march_prices.shape)

# 垂直堆叠三个月份的数据
all_prices = np.vstack((january_prices, february_prices, march_prices))
print("\\n合并后的价格数据形状:", all_prices.shape)
print("总共有", all_prices.shape[0], "天的价格数据")

高级技巧和最佳实践 🎯

掌握一些高级技巧可以让我们的代码更加高效和优雅。

性能优化技巧

当处理大量数组时,合理选择堆叠方法可以显著提升性能:

import numpy as np
import time

# 创建测试数据
arrays = [np.random.rand(1000, 100) for _ in range(10)]

# 方法1: 逐个堆叠
start_time = time.time()
result1 = arrays[0]
for arr in arrays[1:]:
result1 = np.vstack((result1, arr))
time1 = time.time() start_time

# 方法2: 一次性堆叠
start_time = time.time()
result2 = np.vstack(arrays)
time2 = time.time() start_time

print(f"逐个堆叠耗时: {time1:.4f} 秒")
print(f"一次性堆叠耗时: {time2:.4f} 秒")
print(f"性能提升: {time1/time2:.2f} 倍")

错误处理和调试

在实际应用中,良好的错误处理机制非常重要:

import numpy as np

def safe_hstack(*arrays):
"""
安全的水平堆叠函数,包含错误检查
"""

if not arrays:
raise ValueError("至少需要一个数组")

try:
# 检查数组是否为空
for i, arr in enumerate(arrays):
if arr.size == 0:
print(f"警告: 第{i}个数组为空")

# 执行堆叠
result = np.hstack(arrays)
return result

except ValueError as e:
print(f"堆叠失败: {e}")
print("请检查数组的维度是否兼容")
return None

# 测试安全堆叠函数
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
arr3 = np.array([[9], [10]]) # 维度不匹配

result = safe_hstack(arr1, arr2)
if result is not None:
print("成功堆叠:")
print(result)

result = safe_hstack(arr1, arr3)
if result is not None:
print("成功堆叠:")
print(result)

内存管理考虑

处理大型数组时,内存使用是一个重要考虑因素:

import numpy as np

# 创建大型数组
large_arrays = [np.random.rand(10000, 1000) for _ in range(5)]

print("每个数组的内存占用:", large_arrays[0].nbytes / (1024**2), "MB")
print("总内存需求:", len(large_arrays) * large_arrays[0].nbytes / (1024**2), "MB")

# 使用 delete 删除原始数组以释放内存(如果不再需要)
# 注意:这只是示例,在实际应用中要谨慎使用
# del large_arrays

# 更好的做法是使用生成器或分批处理
def process_arrays_in_batches(arrays, batch_size=2):
"""分批处理大型数组"""
for i in range(0, len(arrays), batch_size):
batch = arrays[i:i+batch_size]
yield np.hstack(batch)

# 分批处理示例
small_arrays = [np.random.rand(100, 10) for _ in range(6)]
for i, batch_result in enumerate(process_arrays_in_batches(small_arrays, 3)):
print(f"批次 {i+1} 处理完成,形状: {batch_result.shape}")

与其他堆叠函数的比较 ⚖️

NumPy 提供了多种堆叠函数,了解它们的区别有助于选择最适合的方法。

stack vs hstack/vstack

import numpy as np

# 创建示例数组
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print("原始数组 a:", a)
print("原始数组 b:", b)

# 使用 stack(默认沿新轴堆叠)
stacked_new_axis = np.stack((a, b))
print("\\n使用 stack 的结果:")
print(stacked_new_axis)
print("形状:", stacked_new_axis.shape)

# 使用 vstack(沿现有轴0堆叠)
vstacked = np.vstack((a, b))
print("\\n使用 vstack 的结果:")
print(vstacked)
print("形状:", vstacked.shape)

# 使用 hstack(沿现有轴1堆叠)
hstacked = np.hstack((a, b))
print("\\n使用 hstack 的结果:")
print(hstacked)
print("形状:", hstacked.shape)

concatenate 函数的灵活性

concatenate 是最通用的连接函数,可以指定任意轴进行连接:

import numpy as np

# 创建三维数组
arr1 = np.random.rand(2, 3, 4)
arr2 = np.random.rand(2, 3, 4)

print("数组1形状:", arr1.shape)
print("数组2形状:", arr2.shape)

# 沿不同轴连接
concat_axis0 = np.concatenate((arr1, arr2), axis=0)
print("\\n沿轴0连接后形状:", concat_axis0.shape)

concat_axis1 = np.concatenate((arr1, arr2), axis=1)
print("沿轴1连接后形状:", concat_axis1.shape)

concat_axis2 = np.concatenate((arr1, arr2), axis=2)
print("沿轴2连接后形状:", concat_axis2.shape)

# 等价关系
print("\\n验证等价性:")
print("np.vstack 等价于 np.concatenate(axis=0):",
np.array_equal(np.vstack((arr1, arr2)), concat_axis0))

print("np.hstack 等价于 np.concatenate(axis=1) 对于2D数组:",
np.array_equal(np.hstack((arr1[:, 0, :], arr2[:, 0, :])),
np.concatenate((arr1[:, 0, :], arr2[:, 0, :]), axis=1)))

实战案例分析 📈

让我们通过几个实战案例来深入理解这些函数的应用。

案例1: 构建机器学习特征矩阵

在构建机器学习模型时,经常需要将不同类型的数据特征组合成一个完整的特征矩阵:

import numpy as np

class FeatureBuilder:
"""特征构建器类"""

def __init__(self):
self.features = []

def add_numerical_features(self, data):
"""添加数值型特征"""
if isinstance(data, list):
data = np.array(data)
if data.ndim == 1:
data = data.reshape(1, 1)
self.features.append(data)
return self

def add_categorical_features(self, data, one_hot_encoded=True):
"""添加分类特征"""
if isinstance(data, list):
data = np.array(data)
if data.ndim == 1 and one_hot_encoded:
# 这里简化处理,实际应用中应该使用 sklearn 的 OneHotEncoder
unique_vals = np.unique(data)
encoded = np.zeros((len(data), len(unique_vals)))
for i, val in enumerate(unique_vals):
encoded[data == val, i] = 1
data = encoded
elif data.ndim == 1:
data = data.reshape(1, 1)
self.features.append(data)
return self

def build(self):
"""构建最终的特征矩阵"""
if not self.features:
raise ValueError("没有添加任何特征")
return np.hstack(self.features)

# 使用示例
builder = FeatureBuilder()

# 添加数值型特征
age_data = [25, 30, 35, 28, 42]
income_data = [50000, 75000, 100000, 60000, 120000]

builder.add_numerical_features(age_data)
builder.add_numerical_features(income_data)

# 添加分类特征
gender_data = ['M', 'F', 'M', 'F', 'M']
city_data = ['NYC', 'LA', 'Chicago', 'NYC', 'Boston']

# 这里简化处理,实际应用中应该使用 proper encoding
gender_encoded = np.array([[1, 0], [0, 1], [1, 0], [0, 1], [1, 0]]) # M/F 编码
city_encoded = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 0, 0], [0, 0, 1]]) # 城市编码

builder.features.append(gender_encoded)
builder.features.append(city_encoded)

# 构建特征矩阵
feature_matrix = builder.build()
print("特征矩阵:")
print(feature_matrix)
print("特征矩阵形状:", feature_matrix.shape)
print("特征名称: Age, Income, Gender_M, Gender_F, City_NYC, City_LA, City_Chicago")

案例2: 数据聚合和统计分析

在数据分析中,经常需要将来自不同源的数据进行聚合:

import numpy as np

class DataAggregator:
"""数据聚合器"""

def __init__(self):
self.datasets = []

def add_dataset(self, data, name):
"""添加数据集"""
if isinstance(data, list):
data = np.array(data)
self.datasets.append({'data': data, 'name': name})
return self

def aggregate_by_rows(self):
"""按行聚合(垂直堆叠)"""
if not self.datasets:
raise ValueError("没有数据集")

data_arrays = [ds['data'] for ds in self.datasets]
aggregated = np.vstack(data_arrays)

print("数据聚合信息:")
for i, ds in enumerate(self.datasets):
shape = ds['data'].shape
print(f" {ds['name']}: {shape[0]} 行, {shape[1] if len(shape) > 1 else 1} 列")

return aggregated

def combine_features(self):
"""按列组合(水平堆叠)"""
if not self.datasets:
raise ValueError("没有数据集")

data_arrays = [ds['data'] for ds in self.datasets]
combined = np.hstack(data_arrays)

print("特征组合信息:")
for i, ds in enumerate(self.datasets):
shape = ds['data'].shape
print(f" {ds['name']}: {shape[1] if len(shape) > 1 else 1} 个特征")

return combined

# 模拟销售数据
sales_q1 = np.array([[100, 200, 150],
[120, 180, 160],
[110, 220, 140]])

sales_q2 = np.array([[130, 210, 170],
[140, 190, 180],
[120, 230, 150]])

sales_q3 = np.array([[150, 240, 190],
[160, 200, 200],
[130, 250, 160]])

# 按季度聚合数据
aggregator = DataAggregator()
aggregator.add_dataset(sales_q1, "Q1 Sales").add_dataset(sales_q2, "Q2 Sales").add_dataset(sales_q3, "Q3 Sales")

quarterly_data = aggregator.aggregate_by_rows()
print("\\n季度聚合数据:")
print(quarterly_data)
print("聚合后形状:", quarterly_data.shape)

# 按产品线组合特征
product_a_sales = sales_q1[:, 0:1] # 第一列
product_b_sales = sales_q1[:, 1:2] # 第二列
product_c_sales = sales_q1[:, 2:3] # 第三列

feature_aggregator = DataAggregator()
feature_aggregator.add_dataset(product_a_sales, "Product A").add_dataset(product_b_sales, "Product B").add_dataset(product_c_sales, "Product C")

combined_features = feature_aggregator.combine_features()
print("\\n组合特征数据:")
print(combined_features)
print("组合后形状:", combined_features.shape)

性能基准测试 📊

为了更好地理解不同方法的性能差异,让我们进行一些基准测试:

import numpy as np
import time
import matplotlib.pyplot as plt

def benchmark_stacking_methods():
"""基准测试不同的堆叠方法"""

# 测试不同规模的数据
sizes = [100, 500, 1000, 2000]
hstack_times = []
vstack_times = []
concatenate_times = []

for size in sizes:
# 创建测试数组
arrays_h = [np.random.rand(size, 50) for _ in range(10)]
arrays_v = [np.random.rand(50, size) for _ in range(10)]

# 测试 hstack
start = time.time()
result_h = np.hstack(arrays_h)
hstack_times.append(time.time() start)

# 测试 vstack
start = time.time()
result_v = np.vstack(arrays_v)
vstack_times.append(time.time() start)

# 测试 concatenate
start = time.time()
result_c = np.concatenate(arrays_h, axis=1)
concatenate_times.append(time.time() start)

return sizes, hstack_times, vstack_times, concatenate_times

# 运行基准测试
sizes, h_times, v_times, c_times = benchmark_stacking_methods()

print("性能基准测试结果:")
print("数组大小\\tHStack(s)\\tVStack(s)\\tConcat(s)")
for i, size in enumerate(sizes):
print(f"{size}\\t\\t{h_times[i]:.6f}\\t{v_times[i]:.6f}\\t{c_times[i]:.6f}")

# 可视化结果
plt.figure(figsize=(10, 6))
plt.plot(sizes, h_times, 'o-', label='hstack')
plt.plot(sizes, v_times, 's-', label='vstack')
plt.plot(sizes, c_times, '^-', label='concatenate')
plt.xlabel('数组大小')
plt.ylabel('执行时间 (秒)')
plt.title('不同堆叠方法的性能比较')
plt.legend()
plt.grid(True)
plt.show()

常见问题和解决方案 ❓

在使用 hstack 和 vstack 时,可能会遇到一些常见问题,让我们来看看如何解决它们。

问题1: 维度不匹配

import numpy as np

def fix_dimension_mismatch():
"""解决维度不匹配问题"""

# 问题示例
arr1 = np.array([[1, 2, 3], [4, 5, 6]]) # 2×3
arr2 = np.array([7, 8]) # 1D array

print("原始数组:")
print("arr1 shape:", arr1.shape)
print("arr2 shape:", arr2.shape)

try:
result = np.hstack((arr1, arr2))
except ValueError as e:
print(f"\\n错误: {e}")
print("解决方法1: 调整数组维度")

# 解决方案1: 扩展 arr2 的维度
arr2_reshaped = arr2.reshape(1, 1) # 变为 2×1
result1 = np.hstack((arr1, arr2_reshaped))
print("调整后 arr2 shape:", arr2_reshaped.shape)
print("hstack 结果:")
print(result1)

print("\\n解决方法2: 使用 concatenate 并指定合适的轴")
arr2_broadcast = np.broadcast_to(arr2.reshape(1, 1), (2, 1))
result2 = np.concatenate((arr1, arr2_broadcast), axis=1)
print("concatenate 结果:")
print(result2)

fix_dimension_mismatch()

问题2: 内存不足

import numpy as np
import psutil
import os

def memory_efficient_stacking():
"""内存高效的堆叠方法"""

# 获取当前内存使用情况
def get_memory_usage():
process = psutil.Process(os.getpid())
return process.memory_info().rss / 1024 / 1024 # MB

print(f"初始内存使用: {get_memory_usage():.2f} MB")

# 创建大数组
big_arrays = []
for i in range(5):
arr = np.random.rand(10000, 1000)
big_arrays.append(arr)
print(f"创建第{i+1}个数组后内存: {get_memory_usage():.2f} MB")

# 内存高效的堆叠方式
print("\\n开始堆叠…")
start_mem = get_memory_usage()

# 方法1: 直接堆叠(可能消耗大量内存)
result_direct = np.vstack(big_arrays)
end_mem = get_memory_usage()
print(f"直接堆叠后内存增加: {end_mem start_mem:.2f} MB")
print(f"结果形状: {result_direct.shape}")

# 清理内存
del result_direct
import gc
gc.collect()

# 方法2: 分批处理
def batch_stack(arrays, batch_size=2):
"""分批堆叠以节省内存"""
results = []
for i in range(0, len(arrays), batch_size):
batch = arrays[i:i+batch_size]
batch_result = np.vstack(batch)
results.append(batch_result)
return np.vstack(results)

print("\\n使用分批处理…")
start_mem = get_memory_usage()
result_batch = batch_stack(big_arrays, batch_size=2)
end_mem = get_memory_usage()
print(f"分批处理后内存增加: {end_mem start_mem:.2f} MB")
print(f"结果形状: {result_batch.shape}")

# memory_efficient_stacking() # 注释掉避免运行时间过长

最佳实践总结 ✅

基于前面的学习和实践,总结出以下最佳实践:

设计模式和架构建议

import numpy as np
from typing import List, Union, Optional

class ArrayStacker:
"""数组堆叠器 – 封装最佳实践"""

@staticmethod
def safe_hstack(arrays: List[np.ndarray],
validate_shapes: bool = True,
fill_value: Optional[float] = None) > np.ndarray:
"""
安全的水平堆叠

Parameters:
———–
arrays : List[np.ndarray]
要堆叠的数组列表
validate_shapes : bool
是否验证形状兼容性
fill_value : float, optional
用于填充不匹配维度的值

Returns:
——–
np.ndarray
堆叠后的数组
"""
if not arrays:
raise ValueError("数组列表不能为空")

if len(arrays) == 1:
return arrays[0]

if validate_shapes:
# 验证除最后一维外的所有维度是否匹配
base_shape = arrays[0].shape[:1] if arrays[0].ndim > 1 else ()
for i, arr in enumerate(arrays[1:], 1):
current_shape = arr.shape[:1] if arr.ndim > 1 else ()
if base_shape != current_shape:
if fill_value is not None:
# 尝试自动调整
arrays = ArrayStacker._adjust_dimensions(arrays, fill_value)
else:
raise ValueError(f"数组 {i} 的形状 {current_shape} 与基础形状 {base_shape} 不匹配")

return np.hstack(arrays)

@staticmethod
def safe_vstack(arrays: List[np.ndarray],
validate_shapes: bool = True) > np.ndarray:
"""
安全的垂直堆叠
"""

if not arrays:
raise ValueError("数组列表不能为空")

if len(arrays) == 1:
return arrays[0]

if validate_shapes:
# 验证除第一维外的所有维度是否匹配
base_shape = arrays[0].shape[1:] if arrays[0].ndim > 1 else ()
for i, arr in enumerate(arrays[1:], 1):
current_shape = arr.shape[1:] if arr.ndim > 1 else ()
if base_shape != current_shape:
raise ValueError(f"数组 {i} 的形状 {current_shape} 与基础形状 {base_shape} 不匹配")

return np.vstack(arrays)

@staticmethod
def _adjust_dimensions(arrays: List[np.ndarray], fill_value: float) > List[np.ndarray]:
"""调整数组维度以使其兼容"""
# 找到最大的维度
max_dims = max(arr.ndim for arr in arrays)

adjusted_arrays = []
for arr in arrays:
if arr.ndim < max_dims:
# 增加维度
new_arr = np.expand_dims(arr, axis=tuple(range(max_dims arr.ndim)))
adjusted_arrays.append(new_arr)
else:
adjusted_arrays.append(arr)

return adjusted_arrays

# 使用示例
stacker = ArrayStacker()

# 正常情况
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
result = stacker.safe_hstack([arr1, arr2])
print("正常堆叠结果:")
print(result)

# 自动调整维度的情况
arr3 = np.array([1, 2, 3]) # 1D
arr4 = np.array([[4, 5, 6]]) # 2D
try:
result = stacker.safe_hstack([arr3, arr4], fill_value=0)
print("\\n自动调整维度后堆叠结果:")
print(result)
except Exception as e:
print(f"错误: {e}")

性能优化指南

import numpy as np
import time
from functools import wraps

def timing_decorator(func):
"""计时装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end start:.6f} 秒")
return result
return wrapper

class PerformanceOptimizer:
"""性能优化器"""

@staticmethod
@timing_decorator
def inefficient_method(arrays):
"""低效的方法:逐个堆叠"""
result = arrays[0]
for arr in arrays[1:]:
result = np.vstack((result, arr))
return result

@staticmethod
@timing_decorator
def efficient_method(arrays):
"""高效的方法:一次性堆叠"""
return np.vstack(arrays)

@staticmethod
@timing_decorator
def preallocate_method(arrays):
"""预分配内存的方法"""
total_rows = sum(arr.shape[0] for arr in arrays)
cols = arrays[0].shape[1]
result = np.empty((total_rows, cols), dtype=arrays[0].dtype)

current_row = 0
for arr in arrays:
next_row = current_row + arr.shape[0]
result[current_row:next_row, :] = arr
current_row = next_row

return result

# 性能测试
test_arrays = [np.random.rand(1000, 100) for _ in range(10)]

print("性能比较测试:")
result1 = PerformanceOptimizer.inefficient_method(test_arrays)
result2 = PerformanceOptimizer.efficient_method(test_arrays)
result3 = PerformanceOptimizer.preallocate_method(test_arrays)

# 验证结果一致性
print("结果一致性检查:")
print("方法1和方法2相同:", np.array_equal(result1, result2))
print("方法2和方法3相同:", np.array_equal(result2, result3))

相关资源推荐 📚

为了进一步深入学习 NumPy 和相关的数组操作,推荐以下几个优秀的学习资源:

  • NumPy 官方文档 – 最权威的参考资料,包含了所有函数的详细说明和示例。

  • SciPy Lecture Notes – 免费的科学计算教程,涵盖了 NumPy、SciPy、Matplotlib 等库的综合使用。

  • Python Data Science Handbook – Jake VanderPlas 的经典著作,在线免费阅读,深入介绍了数据科学中的各种技术。

  • 这些资源提供了从基础到高级的各种内容,适合不同层次的学习者。

    总结 🎉

    通过本文的详细介绍,我们深入了解了 NumPy 中 hstack 和 vstack 函数的强大功能和实际应用。这两个函数虽然看似简单,但在数据处理、机器学习、科学计算等领域发挥着重要作用。

    关键要点回顾:

  • hstack 用于水平堆叠(按列连接),适用于特征维度的扩展
  • vstack 用于垂直堆叠(按行连接),适用于样本数量的增加
  • 在使用前要注意数组维度的兼容性
  • 合理选择堆叠方法可以显著提升性能
  • 实际应用中要考虑内存管理和错误处理
  • 学习建议:

    • 从简单的一维数组开始练习,逐步过渡到复杂多维数组
    • 在实际项目中多实践,积累经验
    • 关注性能优化,特别是在处理大数据集时
    • 学会使用相关的调试和错误处理技巧

    NumPy 的数组堆叠功能只是其强大功能的一个方面。随着你对 NumPy 的深入了解,你会发现更多有趣和实用的功能,帮助你在数据科学的道路上走得更远。记住,编程是一门实践的艺术,只有通过不断的练习和应用,才能真正掌握这些工具的力量。


    🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

    赞(0)
    未经允许不得转载:171主机测评 » Python NumPy - 数组的堆叠 hstack 与 vstack 函数的使用
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址