import os
import time
import traceback
import pandas as pd
import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import iplot, init_notebook_mode
import plotly
import plotly.offline as pltoff
import numpy as np
import pandas as pd
# setting offilne
plotly.offline.init_notebook_mode(connected=True)
def isNone(d):
return (d is None or d == 'None' or
d == '?' or
d == '' or
d == 'NULL' or
d == 'null')
class Plotly2Html(object):
"""
"""
def __init__(self, dataset):
self.dataset = dataset
def ppg_single_line_plots(self, name, title):
# dataset = {'x': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# 'y': [5, 4, 1, 3, 11, 2, 6, 7, 19, 20],
# 'z': [12, 9, 0, 0, 3, 25, 8, 17, 22, 5]}
data_g = []
tr_x = go.Scatter(
x=self.dataset['x'],
y=self.dataset['y'],
name='PPG_G',
mode='lines'
)
data_g.append(tr_x)
# tr_z = go.Scatter(
# x=self.dataset['x'],
# y=self.dataset['z'],
# name='z'
# )
# data_g.append(tr_z)
layout = go.Layout(title=title, xaxis={'title': 'number of ppg singnal'}, yaxis={'title': 'ppg value'},
font={
'size': 16,
'family': 'sans-serif'
}, showlegend=True,
legend=dict(
x=0.9,
y=1.1
))
fig = go.Figure(data=data_g, layout=layout)
print(name)
pltoff.plot(fig, filename=name)
def hr_two_line_plots(self, name, title):
# dataset = {'x': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# 'y': [5, 4, 1, 3, 11, 2, 6, 7, 19, 20],
# 'z': [12, 9, 0, 0, 3, 25, 8, 17, 22, 5]}
data_g = []
tr_x = go.Scatter(
x=self.dataset['x'],
y=self.dataset['y'],
name='Products heartRate',
mode='lines'
)
data_g.append(tr_x)
tr_z = go.Scatter(
x=self.dataset['x'],
y=self.dataset['z'],
name='std heartRate',
mode='lines'
)
data_g.append(tr_z)
layout = go.Layout(title=title, xaxis={'title': 'number of samples'}, yaxis={'title': 'heartRate value'},
font={
'size': 16,
'family': 'sans-serif'
}, showlegend=True,
legend=dict(
x=0.9,
y=1.1
))
fig = go.Figure(data=data_g, layout=layout)
print(name)
pltoff.plot(fig, filename=name)
def hr_error_value_two_line_plots(self, name, title):
"""
方便观察不同信号质量下的心率测量的误差值
"""
# dataset = {'x': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# 'y': [5, 4, 1, 3, 11, 2, 6, 7, 19, 20],
# 'z': [12, 9, 0, 0, 3, 25, 8, 17, 22, 5]}
data_g = []
tr_x = go.Scatter(
x=self.dataset['x'],
y=self.dataset['y'],
name='Error value',
mode='lines'
)
data_g.append(tr_x)
tr_z = go.Scatter(
x=self.dataset['x'],
y=self.dataset['z'],
name='PPG Signal quality',
mode='lines'
)
data_g.append(tr_z)
layout = go.Layout(title=title, xaxis={'title': 'number of samples'},
yaxis={'title': 'PPG Signal quality and Error value'},
font={
'size': 16,
'family': 'sans-serif'
}, showlegend=True,
legend=dict(
x=0.9,
y=1.1
))
fig = go.Figure(data=data_g, layout=layout)
print(name)
pltoff.plot(fig, filename=name)
# 生成柱状图
def bar_charts(name):
dataset = {'x': ['Windows', 'Linux', 'Unix', 'MacOS'],
'y1': [45, 26, 37, 13],
'y2': [19, 27, 33, 21]}
data_g = []
tr_y1 = go.Bar(
x=dataset['x'],
y=dataset['y1'],
name='v1'
)
data_g.append(tr_y1)
tr_y2 = go.Bar(
x=dataset['x'],
y=dataset['y2'],
name='v2'
)
data_g.append(tr_y2)
layout = go.Layout(title="bar charts", xaxis={'title': 'x'}, yaxis={'title': 'value'})
fig = go.Figure(data=data_g, layout=layout)
pltoff.plot(fig, filename=name)
# 生成饼图
def pie_charts(name):
dataset = {'labels': ['Windows', 'Linux', 'Unix', 'MacOS', 'Android', 'iOS'],
'values': [280, 25, 10, 100, 250, 270]}
data_g = []
tr_p = go.Pie(
labels=dataset['labels'],
values=dataset['values']
)
data_g.append(tr_p)
layout = go.Layout(title="pie charts")
fig = go.Figure(data=data_g, layout=layout)
pltoff.plot(fig, filename=name)
# 充满区域的图
def filled_area_plots(name):
dataset = {'x': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
'y1': [5, 4, 1, 3, 11, 2, 6, 7, 19, 20],
'y2': [12, 9, 0, 0, 3, 25, 8, 17, 22, 5],
'y3': [13, 22, 46, 1, 15, 4, 18, 11, 17, 20]}
dataset['y1_stack'] = dataset['y1']
dataset['y2_stack'] = [y1 + y2 for y1, y2 in zip(dataset['y1'], dataset['y2'])]
dataset['y3_stack'] = [y1 + y2 + y3 for y1, y2, y3 in zip(dataset['y1'], dataset['y2'], dataset['y3'])]
dataset['y1_text'] = ['%s(%s%%)' % (y1, y1 * 100 / y3_s) for y1, y3_s in
zip(dataset['y1'], dataset['y3_stack'])]
dataset['y2_text'] = ['%s(%s%%)' % (y2, y2 * 100 / y3_s) for y2, y3_s in
zip(dataset['y2'], dataset['y3_stack'])]
dataset['y3_text'] = ['%s(%s%%)' % (y3, y3 * 100 / y3_s) for y3, y3_s in
zip(dataset['y3'], dataset['y3_stack'])]
data_g = []
tr_1 = go.Scatter(
x=dataset['x'],
y=dataset['y1_stack'],
text=dataset['y1_text'],
hoverinfo='x+text',
mode='lines',
name='y1',
fill='tozeroy'
)
data_g.append(tr_1)
tr_2 = go.Scatter(
x=dataset['x'],
y=dataset['y2_stack'],
text=dataset['y2_text'],
hoverinfo='x+text',
mode='lines',
name='y2',
fill='tonexty'
)
data_g.append(tr_2)
tr_3 = go.Scatter(
x=dataset['x'],
y=dataset['y3_stack'],
text=dataset['y3_text'],
hoverinfo='x+text',
mode='lines',
name='y3',
fill='tonexty'
)
data_g.append(tr_3)
layout = go.Layout(title="filled area plots", xaxis={'title': 'x'}, yaxis={'title': 'value'})
fig = go.Figure(data=data_g, layout=layout)
print(name)
pltoff.plot(fig, filename=name)
def data_singnal_quality_plot(data, quality_v_start, quality_v_end):
data_sq0 = data[(data['singnal_quality'] >= quality_v_start) & (data['singnal_quality'] <= quality_v_end)]
ppg_list = []
list(map(lambda x: ppg_list.extend(list(map(eval, x.strip().split(' ')))), data_sq0['ppg'].tolist()))
return ppg_list
def plot_ppg(data):
ppg_0_list = data_singnal_quality_plot(data, 0, 100)
dataset = {'x': [i for i in range(len(ppg_0_list))],
'y': ppg_0_list}
plt2html = Plotly2Html(dataset)
plt2html.ppg_single_line_plots("ppg_html_pig.html", "HeartRate_dayLife_24h PPG singnal")
def plot_heartRate(data):
data = data[(data['heartrate'] > 30) & (data['std_heartrate'] > 30)]
dataset = {'x': [i for i in range(data.shape[0])],
'y': data['heartrate'].values,
'z': data['std_heartrate'].values}
plt2html = Plotly2Html(dataset)
plt2html.hr_two_line_plots("heartRate.html", "HeartRate_dayLife_24h heartRate")
def plot_hr_error(data):
"""
"""
data = data[(data['heartrate'] > 30) & (data['std_heartrate'] > 30)]
data['error_value'] = data['std_heartrate'] - data['heartrate']
dataset = {'x': [i for i in range(data.shape[0])],
'y': data['error_value'].values,
'z': data['singnal_quality'].values}
plt2html = Plotly2Html(dataset)
plt2html.hr_error_value_two_line_plots("PPG_singnal_error_value.html",
"HeartRate_dayLife_24h PPG Singnal Error Value")
ppg_0_list = []
list(map(lambda x: ppg_0_list.extend(list(map(eval, x.strip().split(' ')))), dfLQ_01['ppg'].tolist()))
print("finish")
import matplotlib.pyplot as plt
%matplotlib inline
import pywt
cA1, cD1 = pywt.dwt(ppg_0_list, 'db3') #得到近似值和细节系数
wap = pywt.WaveletPacket(data=ppg_0_list, wavelet='db3')
dataa = wap['a'].data
print(wap['a'].data)
print(len(wap['a'].data))
plt.figure(num='ca')
plt.plot(cA1)
plt.figure(num='cd')
plt.plot(cD1)
plt.figure(num='data')
plt.plot(dataa)
plt.show()
PPG最高频率220HZ,最低40HZ。
from scipy import signal
b, a = signal.butter(8, 0.8, 'lowpass') #配置滤波器 8 表示滤波器的阶数
b, a
(array([ 0.19287327, 1.5429862 , 5.40045169, 10.80090339, 13.50112924,
10.80090339, 5.40045169, 1.5429862 , 0.19287327]),
array([ 1. , 4.78451489, 10.44504107, 13.45771989, 11.12933104,
6.0252604 , 2.0792738 , 0.41721716, 0.0372001 ]))
filtedData = signal.filtfilt(b, a, ppg_0_list) #data为要过滤的信号
filtedData
array([31934.44215563, 47675.25582932, 48259.35356704, ...,
35243.55748356, 35258.72821223, 35263.07347472])
plt.plot(filtedData)
plt.show()
这里假设采样频率为1000hz,信号本身最大的频率为500hz,要滤除100hz以下频率成分,即截至频率为100hz,则wn=2*100/1000=0.2。Wn=0.2
b, a = signal.butter(8, 0.16, 'highpass') #配置滤波器 8 表示滤波器的阶数
filtedData = signal.filtfilt(b, a, ppg_0_list) #data为要过滤的信号
plt.plot(filtedData)
plt.show()
dataset = {'x': [i for i in range(len(filtedData))],
'y': filtedData}
plt2html = Plotly2Html(dataset)
plt2html.ppg_single_line_plots("ppg_highpass_html_pig.html", " PPG singnal highpass")
这里假设采样频率为1000hz,信号本身最大的频率为500hz,要滤除100hz以下,400hz以上频率成分,即截至频率为100,400hz,则wn1=2100/1000=0.2,Wn1=0.2; wn2=2400/1000=0.8,Wn2=0.8。Wn=[0.02,0.8]
b, a = signal.butter(8, [0.16,0.9], 'bandpass') #配置滤波器 8 表示滤波器的阶数 filtedData = signal.filtfilt(b, a, ppg_0_list) #data为要过滤的信号 plt.plot(filtedData) plt.show() dataset = {'x': [i for i in range(len(filtedData))], 'y': filtedData} plt2html = Plotly2Html(dataset) plt2html.ppg_single_line_plots("ppg_bandpass_html_pig.html", " PPG singnal bandpass")
这里假设采样频率为1000hz,信号本身最大的频率为220hz,要滤除40hz以上,400hz以下频率成分,即截至频率为100,400hz,则wn1=2100/1000=0.2,Wn1=0.2; wn2=2400/1000=0.8,Wn2=0.8。Wn=[0.02,0.8],和带通相似,但是带通是保留中间,而带阻是去除。
b, a = signal.butter(8, [0.16,0.85], 'bandstop') #配置滤波器 8 表示滤波器的阶数
filtedData = signal.filtfilt(b, a, ppg_0_list) #data为要过滤的信号
plt.plot(filtedData)
plt.show()
dataset = {'x': [i for i in range(len(filtedData))],
'y': filtedData}
plt2html = Plotly2Html(dataset)
plt2html.ppg_single_line_plots("ppg_bandstop_html_pig.html", " PPG singnal bandstop")
In [17]:
b, a = signal.butter(8, [0.16,0.9], 'bandpass') #配置滤波器 8 表示滤波器的阶数
filtedData = signal.filtfilt(b, a, ppg_0_list) #data为要过滤的信号
import numpy as np#导入一个数据处理模块
import pylab as pl#导入一个绘图模块,matplotlib下的模块
fft_size = 1024 #FFT处理的取样长度
x = filtedData
# N点FFT进行精确频谱分析的要求是N个取样点包含整数个取样对象的波形。因此N点FFT能够完美计算频谱对取样对象的要求是n*Fs/N(n*采样频率/FFT长度),
# 因此对8KHZ和512点而言,完美采样对象的周期最小要求是8000/512=15.625HZ,所以156.25的n为10,234.375的n为15。
xs = x[:fft_size]# 从波形数据中取样fft_size个点进行运算
xf = np.fft.rfft(xs)/fft_size# 利用np.fft.rfft()进行FFT计算,rfft()是为了更方便对实数信号进行变换,由公式可知/fft_size为了正确显示波形能量
# rfft函数的返回值是N/2+1个复数,分别表示从0(Hz)到sampling_rate/2(Hz)的分。
#于是可以通过下面的np.linspace计算出返回值中每个下标对应的真正的频率:
freqs = np.linspace(0, 25, fft_size/2+1)
# np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
#在指定的间隔内返回均匀间隔的数字
xfp = 20*np.log10(np.clip(np.abs(xf), 1e-20, 1e100))
#最后我们计算每个频率分量的幅值,并通过 20*np.log10()将其转换为以db单位的值。为了防止0幅值的成分造成log10无法计算,我们调用np.clip对xf的幅值进行上下限处理
#绘图显示结果
pl.title(u"156.25Hz and 234.375Hz WaveForm And Freq")
pl.subplot(212)
pl.plot(freqs, xfp)
pl.xlabel(u"Freq(Hz)")
pl.subplots_adjust(hspace=0.4)
pl.show()
对于频谱特性不随时间变化的信号,例如引擎、压缩机等机器噪声,可以对其进行长时间的采样,然后分段进行FFT计算,最后对每个频率分量的幅值求其平均值可以准确地测量信号的频谱。
def average_fft(x, fft_size):
"""
average_fft(x, fft_size)对数组x进行fft_size点FFT运算,以dB为单位返回其平均后的幅值。由于x的长度可能不是fft_size的整数倍,
因此首先将其缩短为fft_size的整数倍,然后用reshape函数将其转换为一个二维数组tmp。tmp的第1轴的长度为fft_size:
"""
n = len(x) // fft_size * fft_size
tmp = x[:n].reshape(-1, fft_size)
tmp *= signal.hann(fft_size, sym=0)
xf = np.abs(np.fft.rfft(tmp)/fft_size)
avgf = np.average(xf, axis=0)
return 20*np.log10(avgf)
xf = average_fft(x, 1000)
pl.plot(xf)
pl.show()
https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.stft.html
scipy.signal.stft(x,fs = 1.0,window ='hann',nperseg = 256,noverlap = None,nfft = None,detrend = False,return_onesided = True,boundary ='zeros',padded = True,axis = -1 )
参数: x : array_like 时间序列的测量值
fs : float,可选 x时间序列的采样频率。默认为1.0。
window : str或tuple或array_like,可选 所需的窗口使用。如果window是一个字符串或元组,则传递给它get_window以生成窗口值,默认情况下为DFT-even。有关get_window窗口和所需参数的列表,请参阅。如果window是array_like,它将直接用作窗口,其长度必须是nperseg。默认为Hann窗口。
nperseg : int,可选 每个段的长度。默认为256。
noverlap : int,可选 段之间重叠的点数。如果没有, 。默认为无。指定时,必须满足COLA约束(请参阅下面的注释)。noverlap = nperseg // 2
nfft : int,可选 如果需要零填充FFT,则使用FFT的长度。如果为 None,则FFT长度为nperseg。默认为无。
detrend : str或function或False,可选 指定如何去除每个段的趋势。如果detrend是字符串,则将其作为类型参数传递给detrend 函数。如果它是一个函数,它需要一个段并返回一个去趋势段。如果detrend为False,则不进行去除趋势。默认为False。
return_onesided : bool,可选 如果为True,则返回实际数据的单侧频谱。如果 False返回双面光谱。请注意,对于复杂数据,始终返回双面光谱。默认为 True。
boundary : str或None,可选 指定输入信号是否在两端扩展,以及如何生成新值,以使第一个窗口段在第一个输入点上居中。这具有当所采用的窗函数从零开始时能够重建第一输入点的益处。有效选项是 。对于零填充扩展,默认为“零”。即扩展到了。['even', 'odd', 'constant', 'zeros', None][1, 2, 3, 4][0, 1, 2, 3, 4, 0]nperseg=3
填充 : bool,可选 指定输入信号在末尾是否填充零以使信号精确地拟合为整数个窗口段,以便所有信号都包含在输出中。默认为True。填充发生在边界扩展之后,如果 边界不是None,则填充为True,默认情况下也是如此。
axis : int,可选 计算STFT的轴; 默认值超过最后一个轴(即axis=-1)。
返回:
f : ndarray 采样频率数组。
t : ndarray 段时间数组。
Zxx : ndarray x的 STFT 。默认情况下,Zxx的最后一个轴对应于段时间。
import scipy.signal as signal
def stft(x, **params):
'''
短时傅立叶变换基本思想是将信号加滑动时间窗,并对窗内信号做傅立叶变换,得到信号的时变频谱。
因而它的时间分辨率和频率分辨率受Heisenberg测不准原理约束,一旦窗函数选定,时频分辨率便确定下来。
这就使它对突变信号和非平稳信号的分析存在局限性,因而不是一种动态的分析方法, 不能敏感地反映信号的突变,只适用于对缓变信号的分析。
:param x: 输入信号
:param params: {fs:采样频率;
window:窗。默认为汉明窗;
nperseg: 每个段的长度,默认为256,
noverlap:重叠的点数。指定值时需要满足COLA约束。默认是窗长的一半,
nfft:fft长度,
detrend:(str、function或False)指定如何去趋势,默认为Flase,不去趋势。
return_onesided:默认为True,返回单边谱。
boundary:默认在时间序列两端添加0
padded:是否对时间序列进行填充0(当长度不够的时候),
axis:可以不必关心这个参数}
:return: f:采样频率数组;t:段时间数组;Zxx:STFT结果
1.fs:表示时序信号的采样频域
2.nperseg:表示单个时间段的长度选择,类似于时间窗的概念,对应滑窗概念里的窗长。
3.noverlap:与前一个时间窗的相重合的时间点的长度,类似于滑窗的概念,其实就是相当于是:
Gap = nperseg - noverlap
4.nfft:是计算离散傅里叶变换的点数。 nfft越大,频域的分辨率就越高(分辨率=fs/nfft),但离瞬时频率就越远;noverlap影响时间轴的分辨率,越接近nfft,分辨率越高,相应的冗余就越多,计算量越大,但计算机只要能承受,问题不大。
5.boundary:扩展参数
Sxx:表示最终输出的频域分析的结果,每一行代表在某一频率下所有时间窗的结果,每一列对应的是某一个时间窗下的所有频率的结果。
f:表示输出对应的的频率。对应Sxx的每一行。
t:表示输出对应的时间段。对应Sxx的每一列。
'''
f, t, zxx = signal.stft(x, **params)
return f, t, zxx
import matplotlib.pyplot as plt
def stft_specgram(x, picname=None, **params): #picname是给图像的名字,为了保存图像
f, t, zxx = stft(x, **params)
# cm_light = mpl.colors.ListedColormap(['#FFA0A0','#A0FFA0', '#A0A0FF'])
cm_light = plt.cm.get_cmap('rainbow')
# cm_light=plt.cm.get_cmap('flag')
# cm_light = plt.get_cmap('PiYG')
ct = plt.pcolormesh(t, f, np.abs(zxx), cmap=cm_light,vmin=-2,vmax=2,edgecolors='face')
plt.colorbar(ct)
plt.title('PPG STFT Magnitude')
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.tight_layout()
if picname is not None:
plt.savefig('..\\picture\\' + str(picname) + '.jpg') #保存图像
plt.show()
# plt.clf() #清除画布
pl.plot(zxx)
pl.show()
return t, f, zxx
b, a = signal.butter(10, [0.1,0.9], 'bandpass') #配置滤波器 8 表示滤波器的阶数
filtedData = signal.filtfilt(b, a, ppg_0_list) #data为要过滤的信号
# t, f, zxx = stft_specgram(ppg_0_list,fs=400,nperseg=500,nfft=1000,noverlap=25)
t, f, zxx = stft_specgram(filtedData,fs=440,nperseg=220, noverlap=25)
import seaborn as sns fig = plt.figure(figsize=[20,6]) ax = sns.heatmap(abs(zxx),cmap="gist_earth")
import plotly
import plotly.offline as pltoff
import plotly.plotly as py
import plotly.graph_objs as go
# setting offilne
plotly.offline.init_notebook_mode(connected=True)
dataset = {
'x': [i for i in range(dfLQ_01.shape[0])],
'y': dfLQ_01['heartrate'].values,
'z': dfLQ_01['std_heartrate'].values
}
data_g = []
'''
['Greys', 'YlGnBu', 'Greens', 'YlOrRd', 'Bluered', 'RdBu',
'Reds', 'Blues', 'Picnic', 'Rainbow', 'Portland', 'Jet',
'Hot', 'Blackbody', 'Earth', 'Electric', 'Viridis', 'Cividis']
'''
aes1 = go.Heatmap( z=np.abs(zxx),x=t,y=f,
type = 'heatmap', colorscale = 'Earth')
data_g.append(aes1)
tr_x = go.Scatter(
x= dataset['x'],
y= dataset['y'],
name='Products heartRate',
mode='lines'
)
data_g.append(tr_x)
tr_z = go.Scatter(
x= dataset['x'],
y= dataset['z'],
name='std heartRate',
mode='lines'
)
data_g.append(tr_z)
layout = go.Layout(
title = 'PPG-STFT-Magnitude',
plot_bgcolor='#ffffff',#图的背景颜色
paper_bgcolor='#ffffff',#图像的背景颜色
autosize = True,
width = 1800,
height = 800,
xaxis = dict(
title = 'Times'
),
yaxis = dict(
title = 'Frequency'
),
)
fig = go.Figure(data=data_g, layout=layout)
pltoff.plot(fig, filename="PPG-STFT-Magnitude.html")
import plotly
import plotly.offline as pltoff
import plotly.plotly as py
import plotly.graph_objs as go
# setting offilne
plotly.offline.init_notebook_mode(connected=True)
data_g = []
for item in zxx:
data_g.append(go.Scatter(y =np.real(item),mode = 'lines'))
layout = go.Layout(
title = 'PPG-STFT-Magnitude',
autosize = False,
width = 1800,
height = 800,
# shapes = [cross(230, 530)[0], cross(230, 530)[1], cross(230, 430)[0], cross(230, 430)[1], cross(230, 330)[0], cross(230, 330)[1], cross(300, 530)[0], cross(300, 530)[1], cross(300, 430)[0], cross(300, 430)[1], cross(370, 530)[0], cross(370, 530)[1]],
xaxis = dict(
title = 'Times'
),
yaxis = dict(
title = 'Frequency'
),
)
fig = go.Figure(data=data_g, layout=layout)
pltoff.plot(fig, filename="PPG-STFT-Magnitude.html")
'PPG-STFT-Magnitude.html'
fig = go.Figure(data=[go.Surface(z=np.real(zxx))])
fig.update_traces(contours_z=dict(show=True, usecolormap=True,
highlightcolor="limegreen", project_z=True))
fig.layout.update(title='Mt Bruno Elevation', autosize=False,
scene_camera_eye=dict(x=1.87, y=0.88, z=-0.64),
width=1200, height=900,
margin=dict(l=65, r=50, b=65, t=90)
)
pltoff.plot(fig, filename="PPG-3D-STFT-Magnitude.html")