Инструмент 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, вы можете скачать информацию с Baidu.Эта статья в основном знакомит с использованием asammdf.

2. Получить все имена сигналов и их индексы

chn_db = mdf.channels_db

3. Преобразовать mdf в фреймворк данных

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

В этом методе есть подводный камень.Данные, собранные канапе, допускают разные сигналы и разные частоты, то есть данные не выровнены.В этом случае настройка этого метода вызовет взрыв памяти, поэтому при использовании следует быть внимательным Это. Если время преобразования слишком велико или сообщается об ошибке, Большинство сигналов собираются на разных частотах. Если вы уже знаете, что частота отличается заранее, вы можете использовать метод get только для получения одного сигнала, а затем проанализируйте его или используйте метод, упомянутый ниже, чтобы сначала выполнить выравнивание данных (передискретизацию), а затем выполнить преобразование.

4. Получить группу каналов и канал

Файлы MDF обычно организованы по каналам и группам каналов. Файл может содержать несколько групп каналов, а группа каналов также может содержать несколько каналов. Каналы и сигналы соответствуют друг другу. Каналы хранят некоторую информацию описания, данные и временные метки. сигнал, 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, virtual_groups — это словарь, поэтому мы можем пройти через метод items, а затем получить каналы, содержащиеся в текущей группе каналов, через mdf.included_channels( virtual_group_index), здесь мы заметили, что полученные им каналы представляют собой список кортежей, первый — None, второй и третий — индекс группы каналов и индекс канала соответственно, и смотрим параметры mdf.get() Их много, первые три — это имя сигнала, индекс группы, индекс канала, которые на самом деле представляют собой три кортежа списка, поскольку группа каналов и канал представляют собой двумерную структуру, поэтому на самом деле сигнал может получить без имени сигнала или через индекс группы и индекс канала, а затем передать None в качестве первого параметра.При этом имена каналов в разных группах каналов могут быть одинаковыми, поэтому в настоящее время, если только передается имя сигнала, код сообщит об ошибке, потому что он не знает, какую группу вы получаете сигнал, поэтому вам нужно передать индекс группы и индекс канала в это время, но если вы используете индекс с самого начала, он будет не работает, потому что эти два индекса будут однозначно определять сигнал.

 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 — это статический метод, который может вызываться непосредственно классом, этот метод используется для соединения двух файлов с одной и той же группой и каналом, то есть вертикального соединения. Горизонтальное сшивание данных, содержащих разные сигналы, не поддерживается.

7. Фильтрация данных

mdf.filter(channels), как упоминалось выше, возвращает экземпляр mdf, содержащий указанный сигнал, а не список сигналов.

8. Экспорт данных

asammdf поддерживает преобразование mdf в другие форматы, такие как csv, hdf5, мат, паркет.

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 повешен на Сигнал, поэтому используем mdf.get('signname') для получения сигнала, а также можем рисовать напрямую.Стоит отметить, что сигнал поддерживает прямое вычисление, а дальше смотрим на официальный пример

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 имеет метод повторной выборки. Вот пример

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 секунды данных, а также поддерживает передачу массива для сбора данных в указанный момент времени для повторной выборки После выборки данные выравниваются, и в это время их можно преобразовать в фрейм данных.Для повышения частоты дискретизации есть проблема, требующая внимания.Как правило, 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. Он также поддерживает создание из фрейма данных

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 на самом деле имеет инструмент графического интерфейса, который можно установить с помощью команды pip install asammdf[gui].Графический интерфейс реализует некоторые функции канапе.Он кажется очень мощным.Если вам интересно, вы можете попробовать его.

рекомендация

отblog.csdn.net/zy1620454507/article/details/128017125
рекомендация