python解析mdf或mf4文件利器---asammdf

这篇具有很好参考价值的文章主要介绍了python解析mdf或mf4文件利器---asammdf。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

安装

pip install asammdf

解析

1.读取文件,获取信号

from asammdf import MDF

f = r"xxx.mdf"
mdf = MDF(f)
signal = mdf.get('信号名')
data = signal.samples
timestamps = signal.timestamps

如果对mdf的格式不熟悉,可以百度下资料,本文主要介绍asammdf的使用。

2. 获取所有信号名及其索引

chn_db = mdf.channels_db

3. mdf转成dataframe

#代码接上
df = mdf.to_dataframe()

这个方法有个坑,canape采集数据允许不同信号不同频率,也就是说数据对不齐,这种情况下调这个方法会导致爆内存,所以使用时应该注意一下,如果转换时间过长,或者报错,多数是信号采集频率不同,如果事先就已经知道频率不同,只能用get方法获取单个信号,然后分析,或者用后面讲到的方法先进行数据对齐(重采样),然后在进行转换。

4.获取channelgroup和chnannel

mdf文件一般是用channel和channel group组织的,一个文件可能包含多个chnannel group,一个channel group也可以包含多个channel,channel和signal一一对应,channel保存了一些描述信息,数据和时间戳保存在signal里,asammdf里提供了几个方法来获取信息,下面是一段从源码里摘取的一段

for group_index, (virtual_group_index, virtual_group) in enumerate(
            mdf_ins.virtual_groups.items()
        ):
            if virtual_group.cycles_nr == 0 and empty_channels == "skip":
                continue

            channels = [
                (None, gp_index, ch_index)
                for gp_index, channel_indexes in mdf_ins.included_channels(
                    virtual_group_index
                )[virtual_group_index].items()
                for ch_index in channel_indexes
                if ch_index != mdf_ins.masters_db.get(gp_index, None)
            ]

从中可以看出,我们可以通过mdf.virtual_groups获取到全部的channel group,virtual_groups是一个字典,所以可以通过items方法遍历,然后通过mdf.included_channels(virtual_group_index)方法获取到当前channel group下包含的channels,这里我们注意到,他获取的channels是一个元组列表,第一个都是None,第二个和第三个分别是channel group的index和channel的index,再看mdf.get()方法的参数其实有很多,前三个是signal name,group index,channel index,其实就是列表元组的三个,因为channel group、channel是一个二维结构,所以,其实可以不用信号名来获取信号,也可以通过group index,和channel index,然后第一个参数传None,同时,不同通道组中的通道名字可能相同,所以这时,如果仅仅传信号名,代码会报错,因为它不知道你取得哪个组里的信号,所以这时候就需要传group index和channel index,但是如果一开始就用索引就不会,因为这两个索引会唯一确定一个信号。

 5.获取一部分信号数据

基于上段的通道列表,我们就可以用mdf.select方法筛选出我们需要的信号,信号和通道是一一对应关系,我们可以把这两个东西当成一个东西理解,只不过两种数据结构存的数据不一样,所以这里大家不要混淆或者蒙圈。上面获取的其实是全部的信号,其实也可以是部分。

            old_signals = [
                signal
                for signal in mdf_ins.select(
                    channels, raw=True, copy_master=False, validate=False
                )
            ]

mdf.select返回的是信号列表,而不是包含这些信号的mdf实列,mdf.filter()返回的是mdf实列,这里需要注意下,还有那个raw参数,虽然它这里用的True,但是,一般我们要设置为False,特别是自己处理数据的时候,因为设置成True会导致读出的数据错误,它之所以有这个参数,我怀疑是为了兼容一些数据格式,而不是为了数据准确性。

 6. 数据合并

MDF类里有个静态方法concatenate,用于多个文件的合并。

from asammdf import MDF
mdf1 = MDF(r"f1.mf4")
mdf2 = MDF(r"f2.mf4")

mdf = MDF.concatenate([mdf1,mdf2])

concatenate是一个静态方法,可以用类直接调用,这个方法用于两个group和channel都一样的文件进行连接,即纵向连接。不支持包含不同信号的数据进行横向拼接 。

7. 数据过滤

mdf.filter(channels),这个上文已经提到,它返回的是包含指定信号的mdf实例,而不是信号列表。

8. 数据导出

asammdf支持把mdf转成其它格式,比如csv,hdf5,mat,parquet。

mdf.export(format,filename)

9. 数据绘图

asammdf支持用信号数据绘图,下面是官方示例

# map signals
xs = np.linspace(-1, 1, 50)
ys = np.linspace(-1, 1, 50)
X, Y = np.meshgrid(xs, ys)
vals = np.linspace(0, 180.0 / np.pi, 100)
phi = np.ones((len(vals), 50, 50), dtype=np.float64)
for i, val in enumerate(vals):
    phi[i] *= val
R = 1 - np.sqrt(X**2 + Y**2)
samples = np.cos(2 * np.pi * X + phi) * R

timestamps = np.arange(0, 2, 0.02)

s_map = Signal(
    samples=samples, timestamps=timestamps, name="Variable Map Signal", unit="dB"
)
s_map.plot()

 我们看到plot方法是挂在Signal上的,所以,我们用mdf.get('signame')获取到一个信号,也可以直接绘图,值得注意的是,信号是支持直接计算的,再看一个官方的例子

import numpy as np

from asammdf import Signal

# create 3 Signal objects with different time stamps

# unit8 with 100ms time raster
timestamps = np.array([0.1 * t for t in range(5)], dtype=np.float32)
s_uint8 = Signal(
    samples=np.array([t for t in range(5)], dtype=np.uint8),
    timestamps=timestamps,
    name="Uint8_Signal",
    unit="u1",
)

# int32 with 50ms time raster
timestamps = np.array([0.05 * t for t in range(10)], dtype=np.float32)
s_int32 = Signal(
    samples=np.array(list(range(-500, 500, 100)), dtype=np.int32),
    timestamps=timestamps,
    name="Int32_Signal",
    unit="i4",
)

# float64 with 300ms time raster
timestamps = np.array([0.3 * t for t in range(3)], dtype=np.float32)
s_float64 = Signal(
    samples=np.array(list(range(2000, -1000, -1000)), dtype=np.int32),
    timestamps=timestamps,
    name="Float64_Signal",
    unit="f8",
)

prod = s_float64 * s_uint8
prod.name = "Uint8_Signal * Float64_Signal"
prod.unit = "*"
prod.plot()

pow2 = s_uint8**2
pow2.name = "Uint8_Signal ^ 2"
pow2.unit = "u1^2"
pow2.plot()

allsum = s_uint8 + s_int32 + s_float64
allsum.name = "Uint8_Signal + Int32_Signal + Float64_Signal"
allsum.unit = "+"
allsum.plot()

# inplace operations
pow2 *= -1
pow2.name = "- Uint8_Signal ^ 2"
pow2.plot()

# cut signal
s_int32.plot()
cut_signal = s_int32.cut(start=0.2, stop=0.35)
cut_signal.plot()

 我们可以看到,信号是可以相乘,相加,平方操作的,操作返回应该还是信号类型,因为后面我们看到它调用了plot方法,这个方法是挂在Signal上面的。

10.数据重采样

canape支持不同信号的采集频率不同,如果不同信号数据量不同,也就是说数据不对齐,这样在转成dataframe的时候就会爆内存,再比如频率如果过高的话,数据量必然特别大,这样的话也不利于分析,这时就需要我们对数据进行重新采样。asammdf有个resample方法。下面是个例子

from asammdf import MDF
from utils import get_lines_from_txt
f = r"C:\Users\c-master1\Desktop\下载\AnalysisTools\YIQI_2022-08-06_14-37-50.mf4"
f1 = r"C:\Users\c-master1\Downloads\2022-11-21_16-59-29.mf4"
f2 = r"C:\Users\c-master1\Downloads\Recorder_2022-11-17_14-55-00.MDF"
clist_f = r"C:\Users\c-master1\Downloads\数据分析用信号列表(2)(2).txt"

channel_list = ['ABV_Ki_Out','ACsurge_Flag_b','csABPV_posVlv_perc']
channel_list = get_lines_from_txt(clist_f)
mdf = MDF(f2)
mdf = mdf.filter(channel_list)
signals = mdf.select(channel_list)
max = 0
name = ""
for sig in signals:
    if len(sig.samples) > max:
        max = len(sig.samples)
        name = sig.name
mdf1 = mdf.resample(raster=name)
df1 = mdf1.to_dataframe()
df1

resample方法支持按某个信号进行重采样,即所有其它信号按这个信号对齐,也支持手动写个频率,比如0.1就是0.1秒一个数据,还支持传一个数组,采集指定时间点的数据,进行重采样后数据就对齐了,这时就可以转成dataframe了,对于升采样,有个需要注意的问题,一般mf4保存的是数字,当然也可以是字符串,对于数值型数据,主要分两种,也就是整数和小数,也叫整型和浮点型,在asammdf中,对于整数和小数的默认重采样策略是不同的,整数的话默认是重复之前的值,而对于浮点型也就是小数的话,默认是线性插值,所以,有的时候你会发现重采样后数据好像被改了,其实就是线性插值导致的,如果想改变默认行为,可以用

mdf.configure(float_interpolation=0,integer_interpolation=0)

来改变,其中mdf是MDF的实例,针对浮点型,asammdf支持两种重采样策略,一种是重复之前的值,一种就是线性插值,对于整型,提供了三种策略,前面两种和浮点型一样,还有一种混合策略,就是结合重复前值和线性插值的特点,具体没有试过。还有一个坑需要注意,就是表面上我们看到数据里存的是整数,但是它的类型是Double的,这种asammdf也会把它当浮点型处理,因为它只按数据类型处理,不管你存的具体数据到底是整数还是小数。 

mdf/mf4文件创建

1. mdf支持从零创建mdf/mf4文件,下面是官方示例

# -*- coding: utf-8 -*-
"""
*asammdf* MDF usage example
"""
import numpy as np

from asammdf import MDF, Signal

# create 3 Signal objects

timestamps = np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype=np.float32)

# unit8
s_uint8 = Signal(
    samples=np.array([0, 1, 2, 3, 4], dtype=np.uint8),
    timestamps=timestamps,
    name="Uint8_Signal",
    unit="u1",
)
# int32
s_int32 = Signal(
    samples=np.array([-20, -10, 0, 10, 20], dtype=np.int32),
    timestamps=timestamps,
    name="Int32_Signal",
    unit="i4",
)

# float64
s_float64 = Signal(
    samples=np.array([-20, -10, 0, 10, 20], dtype=np.float64),
    timestamps=timestamps,
    name="Float64_Signal",
    unit="f8",
)

# create empty MDf version 4.00 file
with MDF(version="4.10") as mdf4:

    # append the 3 signals to the new file
    signals = [s_uint8, s_int32, s_float64]
    mdf4.append(signals, comment="Created by Python")

    # save new file
    mdf4.save("my_new_file.mf4", overwrite=True)

    # convert new file to mdf version 3.10
    mdf3 = mdf4.convert(version="3.10")
    print(mdf3.version)

    # get the float signal
    sig = mdf3.get("Float64_Signal")
    print(sig)

    # cut measurement from 0.3s to end of measurement
    mdf4_cut = mdf4.cut(start=0.3)
    mdf4_cut.get("Float64_Signal").plot()

    # cut measurement from start of measurement to 0.4s
    mdf4_cut = mdf4.cut(stop=0.45)
    mdf4_cut.get("Float64_Signal").plot()

    # filter some signals from the file
    mdf4 = mdf4.filter(["Int32_Signal", "Uint8_Signal"])

    # save using zipped transpose deflate blocks
    mdf4.save("out.mf4", compression=2, overwrite=True)

        当然,从官方的示例中还可以获得一些其它信息,比如,文件的裁剪,mdf.cut(**args)方法.

2. 也支持从dataframe创建

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'a': np.arrange(10),
    'b': np.arrange(10),
    'c': np.arrange(10),
})
mdf_f = MDF()
mdf_f.append(df)
mdf_f.save("xx.mf4",overwrite=True)

最后

        asammdf其实还有一个gui工具,用pip install asammdf[gui]命令即可安装,图形界面实现了一些canape的功能,感觉还是很强大的,感兴趣的可以用下试试。文章来源地址https://www.toymoban.com/news/detail-473255.html

到了这里,关于python解析mdf或mf4文件利器---asammdf的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • VBA系列技术资料MF33:VBA_将文本文件转换为Excel

    【分享成果,随喜正能量】一心热枕对待生活,静静的安抚自己内心的急迫和焦虑,你人生的好运,常常在你沉醉于生活时悄悄临门的。。 我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。我的教程

    2024年02月13日
    浏览(48)
  • 提升性能的利器:深入解析SectionReader

    本文将介绍 Go 语言中的 SectionReader ,包括 SectionReader 的基本使用方法、实现原理、使用注意事项。从而能够在合适的场景下,更好得使用 SectionReader 类型,提升程序的性能。 这里我们需要实现一个基本的HTTP文件服务器功能,可以处理客户端的HTTP请求来读取指定文件,并根据

    2024年02月11日
    浏览(31)
  • SQL Server导入.mdf方法,错误处理

     1.右击数据库,点击附加  2.点击添加  3.选择文件,点击确定  4.添加成功!  1.找到要添加的.mdf文件 — 右击 — 属性 — 安全—编辑  2.允许完全控制打√,点击确定  3.找到与.mdf对应的.ldf文件— 右击 — 属性 — 安全—编辑 4. 完全控制,打√,点击确定 5.重复.mdf文件导

    2024年02月05日
    浏览(27)
  • 深入解析Android AIDL:实现跨进程通信的利器

    Android Interface Definition Language (AIDL) 是一种Android系统中的跨进程通信机制。AIDL允许一个应用程序的组件与另一个应用程序的组件通信,并在两者之间传输数据。 AIDL的主要作用是帮助不同进程间共享数据和服务,让他们能够互相调用。例如,在开发一个多功能的音乐播放器时,

    2024年02月19日
    浏览(43)
  • 「Java」《深入解析Java多线程编程利器:CompletableFuture》

    多线程编程是指在一个程序中同时执行多个线程来提高系统的并发性和响应性。在现代计算机系统中,多线程编程已经成为开发者日常工作的一部分。以下是对多线程编程需求和挑战的介绍: 需求: 提高系统的性能:通过同时执行多个线程,可以利用多核处理器的优势,实

    2024年02月11日
    浏览(50)
  • 掌握前端利器:JavaScript页面渲染高阶方法解析与实战

    前端开发中,页面渲染的速度和质量是衡量一个开发者水平的重要标准。而在众多的前端技术中,JavaScript以其强大的页面渲染能力独占鳌头。本文将深入探讨JavaScript在页面渲染中的应用,并通过实例展示其高阶方法,旨在帮助读者更好地掌握前端技术。 JavaScript在页面渲染中

    2024年02月10日
    浏览(41)
  • “深入解析Spring Boot:快速开发Java应用的利器“

    标题:深入解析Spring Boot:快速开发Java应用的利器 摘要:Spring Boot是一个开发Java应用的利器,它简化了Spring应用的配置和部署过程,提供了快速构建和开发Java应用的能力。本文将深入解析Spring Boot的核心特性和优势,并通过示例代码来展示如何使用Spring Boot进行快速应用开发

    2024年02月16日
    浏览(51)
  • Qt 5.14.2 深度解析:打造高效JSON处理利器

    Qt 5.14.2 深度解析:打造高效JSON处理利器 参考资料 Qt官方文档:QJsonDocument Qt官方文档:QJsonObject Qt官方文档:QJsonArray Qt官方文档:QJsonValue 引言 在当今的软件开发世界中,JSON(JavaScript Object Notation)已经成为了数据交换的标准格式。Qt,作为一个跨平台的C++框架,自然也提供

    2024年03月10日
    浏览(49)
  • 【python模块】python解析json文件详解

    JSON(Java Script Object Notation)是一种通常用于以不会“对系统造成负担”的方式传输数据(主要通过 API)的格式。基本原理是利用文本来记录数据点,并将数据点传输给第三方。 JSON是一种使用文本存储数据对象的格式。换句话说,它是一种数据结构,将对象用 文本形式 表示出

    2024年02月08日
    浏览(43)
  • 深入解析Python文件操作:打开文件处理的大门

    在Python中,文件操作是一项常见且重要的任务。通过文件操作,我们可以读取文件内容、写入新的数据以及对文件进行其他的操作。本文将介绍Python中的文件操作,包括打开文件、读取文件、写入文件和关闭文件等基本操作,以及一些常用的文件处理技巧和最佳实践。 在进行

    2024年02月16日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包