我有一些我打算分析的皮肤温度数据(以1Hz收集)。
然而,传感器并不总是与皮肤接触。因此,我有一个挑战,即在保留实际皮肤温度数据的同时去除这些非皮肤温度数据。我有大约100个要分析的文件,所以我需要自动化。
我知道已经有this similar post,但是我无法用它来解决我的问题。
我的数据大致如下:
df =
timeStamp Temp
2018-05-04 10:08:00 28.63
. .
. .
2018-05-04 21:00:00 31.63
我采取的第一步是简单地应用最小阈值 - 这已经摆脱了大部分非皮肤数据。然而,我留下了急剧的跳跃,传感器被移除或连接:
为了消除这些跳跃,我正在考虑采用一种方法,我使用temp的一阶微分,然后使用另一组阈值来摆脱我不感兴趣的数据。
e.g。
df_diff = df.diff(60) # period of about 60 makes jumps stick out
filter_index = np.nonzero((df.Temp <-1) | (df.Temp>0.5)) # when diff is less than -1 and greater than 0.5, most likely data jumps.
然而,我发现自己被困在这里。主要问题是:
1)我不知道现在如何使用此索引列表删除df中的非皮肤数据。怎么做到最好?
更小的问题是 2)我认为我仍然会在边缘附近的数据跳跃中留下一些残留的假象(例如,更严格的阈值将开始消除好的数据)。是否有更好的过滤策略或者摆脱这些文物的方法?
*按照建议进行编辑我还计算了二阶差异,但老实说,我认为第一阶差异将允许更严格的阈值(见下文):
*编辑2:Link to sample data
答案 0 :(得分:2)
尝试下面的代码(我使用切线函数生成数据)。我在评论中使用了Mad Physicist的二阶差分理念。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame()
df[0] = np.arange(0,10,0.005)
df[1] = np.tan(df[0])
#the following line calculates the absolute value of a second order finite
#difference (derivative)
df[2] = 0.5*(df[1].diff()+df[1].diff(periods=-1)).abs()
df.loc[df[2] < .05][1].plot() #select out regions of a high rate-of-change
df[1].plot() #plot original data
plt.show()
以下是输出的缩放,显示已过滤的内容。 Matplotlib从删除数据的开头到结尾绘制一条线。
我相信你的第一个问题是上面的.loc选择。
您的第二个问题将对您的数据集进行一些实验。上面的代码只选择高导数数据。您还需要选择阈值来删除零等。您可以尝试在哪里进行衍生选择。您还可以绘制导数的直方图,以便为您提供有关选择内容的提示。
此外,高阶差分方程可以帮助平滑。这应该有助于删除伪影,而不必修剪切割。
编辑:
可以使用以下方法应用四阶有限差分:
df[2] = (df[1].diff(periods=1)-df[1].diff(periods=-1))*8/12 - \
(df[1].diff(periods=2)-df[1].diff(periods=-2))*1/12
df[2] = df[2].abs()
认为它可能有所帮助是合理的。上述系数可以计算出来或从以下链接导出,以获得更高的订单。 Finite Difference Coefficients Calculator
注意:上述二阶和四阶中心差分方程不是正确的一阶导数。必须除以间隔长度(在这种情况下为0.005)才能得到实际导数。
答案 1 :(得分:0)
以下是针对您的问题
的建议
[...]一种方法,我使用临时的一阶微分,然后使用另一组阈值来摆脱我不感兴趣的数据。
< / LI>- 醇>
[..]我不知道如何使用此索引列表删除df中的非皮肤数据。怎么做到最好?
使用stats.zscore()和pandas.merge()
实际上,您对
的担忧仍然存在一个小问题[...]在边缘附近的数据跳跃中留下了一些残留的人工痕迹[...]
但我们稍后会这样做。
首先,这是一个片段,用于生成与您的数据集共享一些挑战的数据框:
# Imports
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(22)
# A function for noisy data with a trend element
def sample():
base = 100
nsample = 50
sigma = 10
# Basic df with trend and sinus seasonality
trend1 = np.linspace(0,1, nsample)
y1 = np.sin(trend1)
dates = pd.date_range(pd.datetime(2016, 1, 1).strftime('%Y-%m-%d'), periods=nsample).tolist()
df = pd.DataFrame({'dates':dates, 'trend1':trend1, 'y1':y1})
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
# Gaussian Noise with amplitude sigma
df['y2'] = sigma * np.random.normal(size=nsample)
df['y3'] = df['y2'] + base + (np.sin(trend1))
df['trend2'] = 1/(np.cos(trend1)/1.05)
df['y4'] = df['y3'] * df['trend2']
df=df['y4'].to_frame()
df.columns = ['Temp']
df['Temp'][20:31] = np.nan
# Insert spikes and missing values
df['Temp'][19] = df['Temp'][39]/4000
df['Temp'][31] = df['Temp'][15]/4000
return(df)
# Dataframe with random data
df_raw = sample()
df_raw.plot()
正如您所看到的,它们之间有两个不同的尖峰,缺少数字。如果您更愿意隔离差异很大的值,那么它确实是导致问题的缺失数字。第一个峰值不是问题,因为您会发现非常小的数字和与其他数据更相似的数字之间的差异:
但是对于第二个峰值,你将获得一个非常小的数字和一个不存在的数字之间的(不存在的)差异,这样你最终删除的极端数据点是离群值与下一次观察值之间的差异:
对于一次观察,这不是一个大问题。你可以直接填充它。但对于较大的数据集来说,这不是一个非常可行的方法。无论如何,如果您可以在没有该特定值的情况下进行管理,则以下代码可以解决您的问题。你的第一次观察也会遇到类似的问题,但我认为决定是否保留这一价值会更加微不足道。
步骤:
# 1. Get some info about the original data:
firstVal = df_raw[:1]
colName = df_raw.columns
# 2. Take the first difference and
df_diff = df_raw.diff()
# 3. Remove missing values
df_clean = df_diff.dropna()
# 4. Select a level for a Z-score to identify and remove outliers
level = 3
df_Z = df_clean[(np.abs(stats.zscore(df_clean)) < level).all(axis=1)]
ix_keep = df_Z.index
# 5. Subset the raw dataframe with the indexes you'd like to keep
df_keep = df_raw.loc[ix_keep]
# 6.
# df_keep will be missing some indexes.
# Do the following if you'd like to keep those indexes
# and, for example, fill missing values with the previous values
df_out = pd.merge(df_keep, df_raw, how='outer', left_index=True, right_index=True)
# 7. Keep only the first column
df_out = df_out.ix[:,0].to_frame()
# 8. Fill missing values
df_complete = df_out.fillna(axis=0, method='ffill')
# 9. Replace first value
df_complete.iloc[0] = firstVal.iloc[0]
# 10. Reset column names
df_complete.columns = colName
# Result
df_complete.plot()
以下是轻松复制粘贴的全部内容:
# Imports
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(22)
# A function for noisy data with a trend element
def sample():
base = 100
nsample = 50
sigma = 10
# Basic df with trend and sinus seasonality
trend1 = np.linspace(0,1, nsample)
y1 = np.sin(trend1)
dates = pd.date_range(pd.datetime(2016, 1, 1).strftime('%Y-%m-%d'), periods=nsample).tolist()
df = pd.DataFrame({'dates':dates, 'trend1':trend1, 'y1':y1})
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
# Gaussian Noise with amplitude sigma
df['y2'] = sigma * np.random.normal(size=nsample)
df['y3'] = df['y2'] + base + (np.sin(trend1))
df['trend2'] = 1/(np.cos(trend1)/1.05)
df['y4'] = df['y3'] * df['trend2']
df=df['y4'].to_frame()
df.columns = ['Temp']
df['Temp'][20:31] = np.nan
# Insert spikes and missing values
df['Temp'][19] = df['Temp'][39]/4000
df['Temp'][31] = df['Temp'][15]/4000
return(df)
# A function for removing outliers
def noSpikes(df, level, keepFirst):
# 1. Get some info about the original data:
firstVal = df[:1]
colName = df.columns
# 2. Take the first difference and
df_diff = df.diff()
# 3. Remove missing values
df_clean = df_diff.dropna()
# 4. Select a level for a Z-score to identify and remove outliers
df_Z = df_clean[(np.abs(stats.zscore(df_clean)) < level).all(axis=1)]
ix_keep = df_Z.index
# 5. Subset the raw dataframe with the indexes you'd like to keep
df_keep = df_raw.loc[ix_keep]
# 6.
# df_keep will be missing some indexes.
# Do the following if you'd like to keep those indexes
# and, for example, fill missing values with the previous values
df_out = pd.merge(df_keep, df_raw, how='outer', left_index=True, right_index=True)
# 7. Keep only the first column
df_out = df_out.ix[:,0].to_frame()
# 8. Fill missing values
df_complete = df_out.fillna(axis=0, method='ffill')
# 9. Reset column names
df_complete.columns = colName
# Keep the first value
if keepFirst:
df_complete.iloc[0] = firstVal.iloc[0]
return(df_complete)
# Dataframe with random data
df_raw = sample()
df_raw.plot()
# Remove outliers
df_cleaned = noSpikes(df=df_raw, level = 3, keepFirst = True)
df_cleaned.plot()