Python高维度大型气象矩阵存储策略分享

news2024/11/18 21:30:21

零、前情提要

最近需要分析全球范围多变量的数值预报数据,将grb格式的数据下载下来经过一通处理后需要将预处理数据先保存一遍,方便后续操作,处理完发现此时的数据维度很多,数据量巨大使用不同的保存策略的解析难度和储存大小可以相差很大,在此分享下不同存储方式的差异

对比发现,使用ZARR储存高维度大型气象矩阵的储存成本最低,相比于使用pkl存储字典数据小近十倍!

一、两种数据存储策略

1.1 预处理成字典存储

按日期读取各个模式数据,依次提取出各个变量,一个变量一个数组

{
            '20240701': {
                'ec': {
                    'T': [],
                    'rh': [],
                },
                'necp': {
                    'T': [],
                    'rh': [],
                }
            },
            '20240702': {
                'ec': {
                    'T': [],
                    'rh': [],
                },
                'necp': {
                    'T': [],
                    'rh': [],
                }
            },
        }

1.2 预处理为数组存储

预报数据维度
F ( T , m o d e l s , v a r i a b l s , p r e s s u r e s , l a t , l o n ) F(T, models, variabls, pressures, lat, lon) F(T,models,variabls,pressures,lat,lon)

观测场数据维度
O ( T , m o d e l s , p r e s s u r e s , l a t , l o n ) O(T, models, pressures, lat, lon) O(T,models,pressures,lat,lon)

T为时间数,models为模型数量,variables为变量,pressures为气压层,后面两者为经纬度,使用的0.25度分辨率的全球数据,数据量是相当大的

二、pkl储存

  • pkl的特点是可以直接储存python的字典,读取和存储都非常方便
  • 但是占用的文件大小相对很大

2.1 储存策略

pkl擅长储存python字典数据

res = {
            '20240701': {
                'ec': {
                    'T': [],
                    'rh': [],
                },
                'necp': {
                    'T': [],
                    'rh': [],
                }
            },
            '20240702': {
                'ec': {
                    'T': [],
                    'rh': [],
                },
                'necp': {
                    'T': [],
                    'rh': [],
                }
            },
        }

2.2 储存和读取方式

import pickle as pkl
# 存储pkl
def save2pkl(data, save_filepath):
    with open(save_filepath, 'wb') as file:
         pickle.dump(data, file)
     print('finish saving')

def read_pkl(filepath):
    with open(filepath, 'rb') as file:
        data = pkl.load(file)
    return data

2.3 储存大小

结果是使用pkl储存出来的数据量巨大,两个时段的数据量达到3G,是无法接受的
在这里插入图片描述

三、HDF5存储

HDF5既可以通过group的形式储存字典,也可以直接存储numpy数组

3.1 储存策略1(存储大数组)

def save2hdf5(save_filepath, data, compression='gzip'):
      with h5py.File(save_filepath + '.hdf5', 'w') as hf:
          hf.create_dataset('data_name', data=data, compression=compression)
      print('finish saving')

3.2 储存策略2(存储字典)

以上面这个字典为例,通过遍历这个字典的k,v循环存储,需要不断创建group来形成字典的树状结构

data= {
            '20240701': {
                'ec': {
                    'T': [],
                    'rh': [],
                },
                'necp': {
                    'T': [],
                    'rh': [],
                }
            },
            '20240702': {
                'ec': {
                    'T': [],
                    'rh': [],
                },
                'necp': {
                    'T': [],
                    'rh': [],
                }
            },
        }
def save2hdf5(save_filepath, data, compression='gzip'):
    with h5py.File(save_filepath , 'w') as file:
        for date, models in data.items():
            date_group = file.create_group(date)
            for model, variables in models.items():
                model_group = date_group.create_group(model)
                for var_name, var_data in variables.items():
                    model_group.create_dataset(var_name, data=var_data, compression=compression)
    print('finish saving')

这样是针对当前字典写死的代码,还可以用递归解决


def save_dict_to_hdf5(group, data_dict, compression):
    for key, value in data_dict.items():
        if isinstance(value, dict):
            # 创建一个新的 HDF5 组
            subgroup = group.create_group(key)
            # 递归调用
            DataReaderDict.save_dict_to_hdf5(subgroup, value, compression)
        else:
            # 直接保存数据到 HDF5 数据集中
            group.create_dataset(key, data=value, compression=compression)
            
def save2hdf5(data, save_filepath , compression='gzip'):
    with h5py.File(save_filepath + 'hdf5', 'w') as hdf5_file:
        root_group = hdf5_file.create_group('root')
        save_dict_to_hdf5(root_group, data, compression)

3.3 设置压缩策略

在 HDF5 中,压缩策略有几种常见的方法,可以用来减少数据存储空间的需求,通过compression参数设置(来自GPT):

  • gzip: 这是最常用的压缩方法,它使用 DEFLATE 算法进行压缩。Gzip 在压缩率和压缩速度之间提供了一个良好的平衡。可以通过设置压缩级别来调整压缩的强度。

  • szip: 这是 HDF5 提供的另一种压缩方法,特别适用于具有大的数据块的情况。Szip 能够提供更高的压缩比,但可能会比 Gzip 更慢。

  • lzf: 这种压缩算法提供了更快的压缩和解压缩速度,但压缩比通常不如 Gzip 或 Szip 高。它适用于需要快速访问压缩数据的场景。

  • None: 不进行任何压缩。这种策略适用于当压缩不必要或影响性能时的情况

3.4 读取HDF5返回字典

读取方式:通过递归遍历group形成一个dict

def read_hdf5_group(group):
    """
    递归读取 HDF5 组及其所有子组和数据集
    """
    result = {}
    for key, item in group.items():
        if isinstance(item, h5py.Group):
            # 如果是组,递归调用
            result[key] = read_hdf5_group(item)
        elif isinstance(item, h5py.Dataset):
            # 如果是数据集,直接读取数据
            result[key] = item[:]
    return result


def read_hdf5(file_path):
    """
    读取 HDF5 文件,返回字典表示的数据结构
    """
    try:
        with h5py.File(file_path, 'r') as file:
            # 从根组开始递归读取数据
            data = read_hdf5_group(file)
        return data
    except FileNotFoundError as e:
        print(e)
        return None
filepath = r'E:\pythonProject\superensemble\data\combined_data\20240721-20240722-00-24_dict.hdf5'
data = read_hdf5(filepath)
print("a")

3.5 储存大小

  • 使用gzip默认的压缩等级4进行存储,存储字典的大小为1.04GB
    在这里插入图片描述

  • 储存数组的大小为777MB
    在这里插入图片描述

  • 在相同的压缩方式下,直接存储数组的数据大小更小

四、ZARR存储

4.1 储存策略1(储存字典)

递归调用储存

import zarr
def save2zarr(data, save_filepath ):
   zarr_store = zarr.DirectoryStore(save_filepath)
   root = zarr.open(zarr_store, mode='w')
   self.store_dict_to_zarr(root, data)

def store_dict_to_zarr(root, data_dict):
   for key, value in data_dict.items():
       if isinstance(value, dict):
           # 如果值是字典,则创建一个组
           if key not in root:
               root.create_group(key)
           DataReaderDict.store_dict_to_zarr(root[key], value)  # 递归处理子字典
       else:
           # 否则,假设值是数组,创建数据集
           root.create_dataset(key, data=value)

4.2 储存策略2(储存大数组)

直接保存一个数组的方式如下

import zarr
import numpy as np

# 创建一个新的 Zarr 数组
zarr_array = zarr.open('data.zarr', mode='w', 
shape=(100, 100), dtype='f4', chunks=(10, 10))

# 填充数据
data = np.random.random((100, 100))
zarr_array[:] = data

在参数中需要填写数组的维度,存储的数据类型以及分块,压缩策略等等

4.3 设置数据分块以及压缩

Zarr 支持多种压缩算法,如 zlib, gzip, bzip2, lz4, 和 zstd,可以在创建 Zarr 数组时指定压缩方式和参数

import zarr
import numpy as np

# 创建一个带有分块和压缩的 Zarr 数组
compressor = zarr.Blosc(cname='zstd', clevel=3, shuffle=2)  # 使用 zstd 压缩

zarr_array = zarr.open('compressed_data.zarr', mode='w', shape=(100, 100, 100), dtype='f4',
                       chunks=(10, 10, 10), compressor=compressor)

# 填充数据
data = np.random.random((100, 100, 100))
zarr_array[:] = data

4.4 读取zarr返回字典

与读取hdf5类似,通过递归遍历group返回字典

import zarr
def read_zarr_group(group):
    """
    递归读取 Zarr 组及其所有子组和数据集
    """
    result = {}
    for key, item in group.items():
        if isinstance(item, zarr.Group):
            # 如果是组,递归调用
            result[key] = read_zarr_group(item)
        elif isinstance(item, zarr.Array):
            # 如果是数组,直接读取数据
            result[key] = item[:]
    return result

def read_zarr(file_path):
    """
    读取 Zarr 文件,返回字典表示的数据结构
    """
    try:
        store = zarr.DirectoryStore(file_path)
        root_group = zarr.open(store, mode='r')
        # 从根组开始递归读取数据
        data = read_zarr_group(root_group)
        return data
    except FileNotFoundError as e:
        print(e)
        return None
filepath = 'your filepath'
data = read_zarr(filepath)

4.5 储存大小

相同的数据,如果以字典存储,即使用zarr,也有882MB
在这里插入图片描述

但如果合理读取成一个大数组,则只有378MB,远远小于存储字典
在这里插入图片描述

五、小节

  • 对比发现使用zarr存储高纬度的网格数据占用的空间最小,容量为用pkl存储字典的1/10,一旦时间维度拉长使用字典存储的占用可能会大大增加
  • 对于时间序列的气象数据,预处理阶段处理成矩阵数组比处理成字典更加省空间且在下一阶段的操作更方便
  • 在下一阶段的计算方面,使用多维矩阵也可以方便选出各种数据,同时使用矩阵运算运用numpy的一些方法可以大大减少各种循环,非常高校
  • 因此,数据维度较为规整的情况下,尽量读取储存成矩阵数组的形式我认为更优

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1955430.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信息安全技术解析

在信息爆炸的今天,信息技术安全已成为社会发展的重要基石。随着网络攻击的日益复杂和隐蔽,保障数据安全、提升防御能力成为信息技术安全领域的核心任务。本文将从加密解密技术、安全行为分析技术和网络安全态势感知技术三个方面进行深入探讨,…

C++笔试强训9

文章目录 一、选择题1-5题6-10题 二、编程题题目一题目二 一、选择题 1-5题 函数形参是个int类型的引用,传参的时候直接传一个int类型的变量就行 故选A 1.malloc/calloc/realloc—>free 2. new / delete 3. new[] / delete[] 一定要匹配起来使用,否则…

猫头虎分享:Numpy知识点一文带你详细学习np.random.randn()

🐯 猫头虎分享:Numpy知识点一文带你详细学习np.random.randn() 摘要 Numpy 是数据科学和机器学习领域中不可或缺的工具。在本篇文章中,我们将深入探讨 np.random.randn(),一个用于生成标准正态分布的强大函数。通过详细的代码示…

ADS 使用教程(二十九)Understanding Bounding Area Layer for FEM

上一篇:ADS 使用教程(二十八)Working with FEM Mesh & Field Data in ADS 这一节,我们来一起了解一下有限元法(FEM)中的边界区域层(Bounding Area Layer),这是定义仿…

python项目实例和源码权限管理系统

✌网站介绍:✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围:Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容:免费功能设计、免费提供开题答辩P…

浅谈HOST,DNS与CDN

首先这个是网络安全的基础,需得牢牢掌握。 1.什么是HOST HOSTS文件: 定义: HOSTS文件是一个操作系统级别的文本文件,通常位于操作系统的系统目录中(如Windows系统下的C:\Windows\System32\drivers\etc\hosts&#xf…

黑马头条vue2.0项目实战(二)——登录注册功能的实现

1. 布局结构 目标 能实现登录页面的布局 能实现基本登录功能 能掌握 Vant 中 Toast 提示组件的使用 能理解 API 请求模块的封装 能理解发送验证码的实现思路 能理解 Vant Form 实现表单验证的使用 这里主要使用到三个 Vant 组件: NavBar 导航栏 Form 表单 F…

windows 安装 Linux 子系统 Ubuntu,并编译安装nginx

1. 安装Ubuntu 首先可以在 Microsoft Store 自行搜索安装 Ubuntu,个人建议安装 22 版本的即可。Ubuntu安装完成后,以管理员身份打开CMD,运行如下命令: wsl --install 此时打开Ubuntu已经可以正常使用了。 2. 安装C/C编译器 对于…

动态规划专题:线性dp、背包问题,区间

目录 方块与收纳盒 舔狗舔到最后一无所有 可爱の星空 数字三角形 花店橱窗 [NOI1998]免费馅饼 [NOIP2002]过河卒 [NOIP2008]传球游戏 「木」迷雾森林 [NOIP2004]合唱队形 [NOIP1999]拦截导弹 数学考试 小A买彩票 购物 牛牛的旅游纪念品 [NOIP2001]装箱问题 [N…

网络轮询器 NetPoller

网络轮询器 NetPoller 网络轮询器是 Go 语言运行时用来处理 I/O 操作的关键组件,它使用了操作系统提供的 I/O 多路复用机制增强程序的并发处理能力。网络轮询器不仅用于监控网络 I/O,还能用于监控文件的 I/O,它利用了操作系统提供的 I/O 多路…

How can I fix my Flask server‘s 405 error that includes OpenAi api?

题意:解决包含OpenAI API的Flask服务器中出现的405错误(Method Not Allowed,即方法不允许) 问题背景: Im trying to add an API to my webpage and have never used any Flask server before, I have never used Java…

MATLAB进阶:函数和方程

经过前几天的学习,matlab基础我们已经大致了解,现在我们继续学习matlab更进一步的应用。 常用函数 在求解有关多项式的计算时,我们无可避免的会遇到以下几个函数 ypolyval(p,x):求得多项式p在x处的值y,x可以是一个或…

ComfyUI反推提示词节点报错:Load model failed

🎠报错现象 反推提示词的时候会提示报错: Error occurred when executing WD14Tagger|pysssss: [ONNXRuntimeError] : 3 : NO_SUCHFILE : Load model from F:\ComfyUI-aki\custom_nodes\ComfyUI-WD14-Tagger\models\wd-v1-4-convnext-tagger-v2.onnx fa…

创建mysql库,及webserver使用编译

首先安装mysql sudo apt update sudo apt install mysql-server sudo systemctl status mysql #检查mysql是否安装成功 sudo mysql #进入mysqlSHOW DATABASES; create database yourdb; #创建一个名为yourdb的数据库USE yourdb; #使用刚才创建好的数据库 CREATE TABLE …

Go语言----reflect.DeepEqual函数

在使用go语言进行编程的时候,我们通常会对模块进行测试,在测试的过程中,经常会使用reflect.DeepEqual函数,这个函数是在reflect包中,其提供了运行时反射机制的标准库。其中的reflect.DeepEqual()函数是用来比较两个值是…

【知识梳理】Shell的变量计算

转载说明:如果您喜欢这篇文章并打算转载它,请私信作者取得授权。感谢您喜爱本文,请文明转载,谢谢。 Shell中有很多变量的计算,会用到多种运算符。例如这几种: 1. Shell中常见的算术运算符 运算符意义&…

springboot-定时任务源码分析

springboot-定时任务源码分析 前言我们都知道开启 springboot的定时任务需要先使用 EnableScheduling 注解,在可以开启,那么 EnableScheduling 就是定时任务的源头,所以先从 EnableScheduling 开始分析 EnableScheduling 这个注解核心就是…

基于cubeMX的STM32的定时器使用

1、设置cubeMX 这里使用STM32F103RCT6芯片,以定时器2为例,时钟源选择内部时钟 参数设置,预分频7200,定时器周期10000,则表示定时1秒钟 打开定时器2通用中断 其他设置不用修改。时钟页面配置如下 最后生成代码。 2、在…

05.java中常用的类

1.包装类 基本类型包装类booleanBooleancharCharacterbyteByteintIntegerlongLongfloatFloatdoubleDoubleshortShort 从byte开始的包装类都是继承的Number,然后Number继承的object 从byte上面的都是直接继承的oblect (1).装箱和拆箱 装箱:基本类型--…

DSP教学实验箱_数字图像处理操作_案例分享:5-13 灰度图像二值化

一、实验目的 学习灰度图像二值化的原理,掌握图像的读取方法,并实现在LCD上显示二值化前后的图像。 二、实验原理 图像二值化 图像的二值化处理就是将图像上的点的灰度置为 0 或 255,也就是将整个图像呈现出明显的黑白效果。即将 256 个亮…