Python分析离散心率信号(上)

一些理论和背景

心率包含许多有关信息。如果拥有心率传感器和一些数据,那么当然可以购买分析包或尝试一些可用的开源产品,但是并非所有产品都可以满足需求。也是这种情况。那么,为什么不尝试自己做一个人呢?如果正在阅读本文,那么可能想尝试一下。本文适合那些有兴趣了解更多关于心率分析以及如何使用一些基本模块用Python编写简单但有效的分析算法的人。在谈论心率信号的形状时,每个搏动都具有QRS复数的特征,如a。,I-III(QRS)所示。还有一个P波(IV)和一个T波(V)。R成分是大峰(II)。这是感兴趣的一个each beat is characterized by a QRS-complex as shown below in a.), I-III (Q-R-S). There is also a P-wave (IV), and a T-wave (V). The R component is the large peak (II). This is the one of interest to us::

在光体积描记图信号(PPG)中,信号略有不同。PPG信号显示在图像的b。)中。这里的组成部分是舒张压峰值(I)和舒张压峰值(III),舒张压峰值是最高的血压点。这两个波被所谓的重症陷波(II)分开。在这种情况下,收缩峰(I)用于心率提取。

为了开发峰值检测算法,这是一个很好的巧合:对于两种信号,都需要标记每个复合物中的最高峰。


首先,

首先让下载数据集并绘制信号图,以便对数据有所了解并开始寻找有意义地分析数据的方法。将熊猫用于大多数数据任务,并将matplotlib用于大多数绘图需求。

import pandas as pd

import matplotlib.pyplot as plt

dataset = pd.read_csv("data.csv") #Read data
from CSV datafile

plt.title("Heart Rate
Signal") #The title of our plot

plt.plot(dataset.hart) #Draw the plot
object

plt.show() #Display the
plot

复制

信号看起来很干净。请记住,即使使用良好的传感器,也并非总是如此,尤其是在实验室外进行测量时!以后会分析如何处理噪声和自动确定信号质量。


检测第一峰

第一步是找到所有R峰的位置。为此,需要确定感兴趣区域(ROI),即信号中的每个R峰。得到这些之后,需要确定最大值。有几种方法可以做到这一点:

§ 
在ROI数据点上拟合曲线,求解最大值的x位置;

§ 
确定ROI中每组点之间的斜率,找到斜率反转的位置;

§ 
在ROI内标记数据点,找到最高点的位置。

后两种方法在计算上便宜得多,但精度较低。这些方法的高精度比曲线拟合方法更加依赖于高采样率。毕竟,曲线的实际最大值可能会位于两个数据点之间,而不是位于实际数据点上,如果采样率较高,则误差容限将减小。还将使用曲线拟合方法来更精确地近似R峰,因为现在,将ROI中最高点的位置用作拍子的位置。

现在开始工作:首先将不同的峰彼此分离。为此,绘制一个移动平均线,标记心率信号位于移动平均线上方的ROI,并最终在每个ROI中找到最高点,如下所示:

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

import math

dataset = pd.read_csv("data.csv")

#Calculate moving average with 0.75s
in both directions, then append do dataset

hrw = 0.75 #One-sided
window size, as proportion of the sampling frequency

fs = 100 #The example
dataset was recorded at 100Hz

mov_avg = dataset['hart'].rolling(int(hrw*fs)).mean() #Calculate
moving average

#Impute where moving average function
returns NaN, which is the beginning of the signal where x hrw

avg_hr = (np.mean(dataset.hart))

mov_avg = [avg_hr if math.isnan(x) else x for x in mov_avg]

mov_avg = [x*1.2 for x in mov_avg] #For now we
raise the average by 20% to prevent the secondary heart contraction from
interfering, in part 2 we will do this dynamically

dataset['hart_rollingmean'] = mov_avg #Append the
moving average to the dataframe

#Mark regions of interest

window = []

peaklist = []

listpos = 0 #We use a
counter to move over the different data columns

for datapoint in dataset.hart:

rollingmean = dataset.hart_rollingmean[listpos] #Get local
mean

if (datapoint < rollingmean) and (len(window) < 1): #If no
detectable R-complex activity -> do nothing

listpos += 1

elif (datapoint > rollingmean): #If signal
comes above local mean, mark ROI

window.append(datapoint)

listpos += 1

else: #If signal
drops below local mean -> determine highest point

maximum = max(window)

beatposition = listpos - len(window) + (window.index(max(window))) #Notate the
position of the point on the X-axis

peaklist.append(beatposition) #Add detected
peak to list

window = [] #Clear marked
ROI

listpos += 1

ybeat = [dataset.hart[x] for x in peaklist] #Get the
y-value of all peaks for plotting purposes

plt.title("Detected
peaks in signal")

plt.xlim(0,2500)

plt.plot(dataset.hart, alpha=0.5, color='blue') #Plot
semi-transparent HR

plt.plot(mov_avg, color ='green') #Plot moving
average

plt.scatter(peaklist, ybeat, color='red') #Plot detected
peaks

plt.show()

复制


已经在信号中标记了每个R复合体的最高点,还不错!但是,这是一个理想的信号。将了解如何处理噪声,测量误差,检测误差以及如何防止模块在质量差的信号部分抛出误差。

有人可能会问:为什么要移动平均线,为什么不仅仅使用700左右的水平线作为阈值?这是一个有效的问题,可以很好地处理此信号。为什么使用移动平均线与理想化程度较低的信号有关。R峰值的幅度会随时间变化,尤其是当传感器稍微移动时。较小的次要峰的振幅也可以独立于R峰的振幅而变化,有时具有几乎相同的振幅。减少错误检测到的峰的一种方法是通过拟合不同高度处的移动平均值并确定哪种拟合效果最佳。稍后再详细介绍。


计算心率

知道每个峰值在时间上的位置,因此计算此信号的平均“每分钟心跳数”(BPM)度量很简单。只需计算峰之间的距离,取平均值并转换为每分钟的值,如下所示:

RR_list = []

cnt = 0

while (cnt < (len(peaklist)-1)):

RR_interval = (peaklist[cnt+1] - peaklist[cnt]) #Calculate distance
between beats in # of samples

ms_dist = ((RR_interval / fs) * 1000.0) #Convert
sample distances to ms distances

RR_list.append(ms_dist) #Append to
list

cnt += 1

bpm = 60000 / np.mean(RR_list) #60000 ms (1
minute) / average R-R interval of signal

print "Average
Heart Beat is: %.01f" %bpm #Round off to
1 decimal and print

复制

还要更新绘图方法以在图例中显示BPM:

plt.title("Detected
peaks in signal")

plt.xlim(0,2500)

plt.plot(dataset.hart, alpha=0.5, color='blue', label="raw
signal") #Plot semi-transparent HR

plt.plot(mov_avg, color ='green', label="moving
average") #Plot moving average

plt.scatter(peaklist, ybeat, color='red', label="average:
%.1f BPM" %bpm) #Plot detected
peaks

plt.legend(loc=4, framealpha=0.6)

plt.show()

复制

已迈出分析心率信号的第一步。每分钟的心跳数是一种非常有用的量度,在科学研究中经常使用,并且还有许多非科学用途,但是信号包含的信息多得多。将处理从心率信号中提取更复杂的信息。


四舍五入

最后,整理一下代码并将其放入可调用函数中。这将使生活在下一部分变得更加轻松,并且代码将更加有条理和可重用。请注意,可能要做的是整洁的事情,使函数成为类的一部分,但为了使本教程也可供那些不太熟悉Python(并且可能对类不熟悉或不熟悉)的人访问,选择从此处省略本教程系列中的所有代码。

让将BPM值和计算出的列表放在一个可以调用的字典中,并可以附加将在第2部分中计算出的度量。还让编写一个包装函数process(),以便可以使用尽可能少的代码来调用分析:

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

import math

measures = {}

def get_data(filename):

dataset = pd.read_csv(filename)

return dataset

def rolmean(dataset, hrw, fs):

mov_avg = dataset['hart'].rolling(int(hrw*fs)).mean()

avg_hr
= (np.mean(dataset.hart))

mov_avg = [avg_hr if math.isnan(x) else x for x in mov_avg]

mov_avg = [x*1.2 for x in mov_avg]

dataset['hart_rollingmean'] = mov_avg

def detect_peaks(dataset):

window = []

peaklist = []

listpos = 0

for datapoint in dataset.hart:

rollingmean = dataset.hart_rollingmean[listpos]

if (datapoint < rollingmean) and (len(window) < 1):

listpos += 1

elif (datapoint > rollingmean):

window.append(datapoint)

listpos += 1

else:

maximum = max(window)

beatposition = listpos - len(window) + (window.index(max(window)))

peaklist.append(beatposition)

window = []

listpos += 1

measures['peaklist'] = peaklist

measures['ybeat'] = [dataset.hart[x] for x in peaklist]

def calc_RR(dataset, fs):

RR_list = []

peaklist = measures['peaklist']

cnt = 0

while (cnt < (len(peaklist)-1)):

RR_interval = (peaklist[cnt+1] - peaklist[cnt])

ms_dist = ((RR_interval / fs) * 1000.0)

RR_list.append(ms_dist)

cnt += 1

measures['RR_list'] = RR_list

def calc_bpm():

RR_list = measures['RR_list']

measures['bpm'] = 60000 / np.mean(RR_list)

def plotter(dataset, title):

peaklist = measures['peaklist']

ybeat = measures['ybeat']

plt.title(title)

plt.plot(dataset.hart, alpha=0.5, color='blue', label="raw
signal")

plt.plot(dataset.hart_rollingmean, color ='green', label="moving
average")

plt.scatter(peaklist, ybeat, color='red', label="average:
%.1f BPM" %measures['bpm'])

plt.legend(loc=4, framealpha=0.6)

plt.show()

def process(dataset, hrw, fs): #Remember; hrw
was the one-sided window size (we used 0.75) and fs was the sample rate (file
is recorded at 100Hz)

rolmean(dataset, hrw, fs)

detect_peaks(dataset)

calc_RR(dataset, fs)

calc_bpm()

plotter(dataset, "My
Heartbeat Plot")

复制

这样调用:

import heartbeat as hb #Assuming we
named the file 'heartbeat.py'

dataset = hb.get_data("data.csv")

hb.process(dataset, 0.75, 100)

#We have imported our Python module as
an object called 'hb'

#This object contains the dictionary
'measures' with all values in it

#Now we can also retrieve the BPM
value (and later other values) like this:

bpm = hb.measures['bpm']

#To view all objects in the
dictionary, use "keys()" like so:

print hb.measures.keys()

复制

请注意,将get_data()函数与包装器分开。这样,模块还可以接受已经存储在内存中的数据帧对象,例如在由另一个程序生成这些数据帧对象时很有用。这使模块保持灵活。

Python分析离散心率信号(上)的更多相关文章

  1. Python分析离散心率信号(下)

    Python分析离散心率信号(下) 如何使用动态阈值,信号过滤和离群值检测来改善峰值检测. 一些理论和背景 到目前为止,一直在研究如何分析心率信号并从中提取最广泛使用的时域和频域度量.但是,使用的信号 ...

  2. Python分析离散心率信号(中)

    Python分析离散心率信号(中) 一些理论和背景 心率信号不仅包含有关心脏的信息,还包含有关呼吸,短期血压调节,体温调节和荷尔蒙血压调节(长期)的信息.也(尽管不总是始终如一)与精神努力相关联,这并 ...

  3. 五月天的线上演唱会你看了吗?用Python分析网友对这场线上演唱会的看法

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:CDA数据分析师 豆瓣9.4分!这场线上演唱会到底多好看? 首先让我 ...

  4. Python标准库07 信号 (signal包,部分os包)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在了解了Linux的信号基础之后,Python标准库中的signal包就很容易学习 ...

  5. Python语言在企业级应用上的十大谬误

    英文原文:https://www.paypal-engineering.com/2014/12/10/10-myths-of-enterprise-python/ 翻译原文:http://www.os ...

  6. 【转载】帧缓冲驱动程序分析及其在BSP上的添加

    原文地址:(四)帧缓冲驱动程序分析及其在BSP上的添加 作者:gfvvz 一.BSP修改及其分析   1. BSP中直接配置的四个寄存器 S3C6410数据手册的第14.5部分是显示控制器的编程模型部 ...

  7. 用Python分析国民生产总值

    今天学习Python的matplotlib,书上的一道例题感觉写的很好(黄红梅的Python数据分析与应用),就是 利用matplotlib的各种图来对2000-2017年季度生产总值进行分析,这道题 ...

  8. Python分析盘点2019全球流行音乐:是哪些歌曲榜单占领了我们?

    写在前面:圣诞刚过,弥留者节日气息的大家是否还在继续学习呐~在匆忙之际也不忘给自己找几首好听的歌曲放松一下,缠绕着音乐一起来看看关于2019年流行音乐趋势是如何用Python分析的吧! 昨天下午没事儿 ...

  9. Python分析数据难吗?某科技大学教授说,很难但有方法就简单

    用python分析数据难吗?某科技大学的教授这样说,很难,但要讲方法,主要是因为并不是掌握了基础,就能用python来做数据分析的. 所谓python的基础,也就是刚入门的python学习者,学习的基 ...

随机推荐

  1. poj2987最大权闭包(输出最少建塔个数)

    题意:      公司要裁员,每个员工被裁掉之后都会有一定的收益(正或者负),有一些员工之间有限制关系,就是裁掉谁之前必须要先裁掉另一个人,问公司的最大收益和最大收益前提下的最小裁员人数? 思路:   ...

  2. Open VAS 漏扫工具的安装

    wget -q -O - http://www.atomicorp.com/installers/atomic |sh .安装openvas [root@localhost ~]#yum -y ins ...

  3. docker文件系统分层存储原理

    一,前言 众所周知,docker镜像技术的基础是联合文件系统(UnionFS),其文件系统是分层的,那它的分层机制是什么样的呢?共分为几种层呢?又是怎么工作的呢? 目前docker支持的联合文件系统有 ...

  4. 【axios】get/post请求params/data传参总结

    axios中get/post请求方式 1. 前言 最近突然发现post请求可以使用params方式传值,然后想总结一下其中的用法. 2.1 分类 get请求中没有data传值方式 2.2 get请求 ...

  5. Kafka源码分析(一) - 概述

    系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 实际问题 二. 什么是Kafka, 如何解决这些问题的 三. 基本原理 1. 基本 ...

  6. Eclipse的安装及相关配置

    一.Eclipse的下载 二.Eclipse的安装 三.Eclipse中相关配置 一.Eclipse的下载 Eclipse各版本下载地址 1.选择要下载的版本(我这里选择的4.14版) 2.选择与自己 ...

  7. 名称空间 反向解析 reverse

  8. 如何设计一个高性能 Elasticsearch mapping

    目录 前言 mapping mapping 能做什么 Dynamic mapping dynamic=true dynamic=runtime dynamic=false dynamic=strict ...

  9. [Java] 数据分析--数据预处理

    数据结构 键-值对:HashMap 1 import java.io.File; 2 import java.io.FileNotFoundException; 3 import java.util. ...

  10. [bug] Error updating database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MyS

    sql语句写错了,如图,where前多了个逗号