|
| 1 | +原文:[Using Predictive Algorithms to Track Real Time Health Trends](http://blog.algorithmia.com/predictive-algorithms-track-real-time-health-trends/) |
| 2 | + |
| 3 | +--- |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +_这是由Chris Hannam,一个专业的Python和Java开发者,发表的客座文章。想要贡献你自己的how-to文章吗》让我们知道,[在这里联系我们](https://algorithmia.com/contact)。_ |
| 8 | + |
| 9 | +我们已经展示了如何使用预测算法来[跟踪经济发展](http://blog.algorithmia.com/2015/05/tracking-economic-development-with-open-data-and/)。在本教程中,我们将构建一个实时健康显示面板,用来追踪一个人的血压读数,进行时间序列分析,然后使用预测算法绘制时间趋势。本教程是使用时间序列算法和预测API来创建你个人健康显示面板的起点。 |
| 10 | + |
| 11 | +我们会在Python中创建这个现实面板,对我们的数据使用[Withings API](http://oauth.withings.com/api),使用来自[Algorithmia](https://algorithmia.com/)的[预测](https://algorithmia.com/algorithms/TimeSeries/Forecast)和[简单移动平均](https://algorithmia.com/algorithms/TimeSeries/SimpleMovingAverage)微服务,以及[Plotly](https://plot.ly?ref=algorithmia)来绘制数据。 |
| 12 | + |
| 13 | +**tl;dr** 这里是[使用Python对Withings数据运行Algorithmia任务](https://github.com/bassdread/AlgorithmiaWithWithings)的GitHub repo。 |
| 14 | + |
| 15 | +## 测量什么,管理什么 |
| 16 | + |
| 17 | +为什么是血压数据?我的一个朋友被诊断为高血压,并决心用数据来降低它。根据[疾病预防控制中心统计数据](http://www.cdc.gov/dhdsp/data_statistics/fact_sheets/fs_bloodpressure.htm),有多大三分之一的美国人患有高血压,这将带来患[心脏疾病](http://www.cdc.gov/heartdisease)和[中风](http://www.cdc.gov/stroke)的高风险。 |
| 18 | + |
| 19 | +我是一个Python程序员,认为我可以构建一个而简单无服务器的健康显示面板来帮助我的朋友测量和了解他的血压。 |
| 20 | + |
| 21 | +第一步是使用便宜的血压监视器和[Withings应用](https://play.google.com/store/apps/details?id=com.withings.wiscale2)来建立测量血压并记录的例行程序。然后,我们将使用Withings API,为健康显示面板访问我们的数据 (Withings还为那些不想手动记录他们的数据的人搞了一个[启用wifi功能的血压袖带](https://www.withings.com/us/en/products/blood-pressure-monitor))。 |
| 22 | + |
| 23 | +我的朋友已经在记录过去五个月内早上和夜间的心脏率、收缩压和舒张压。下面是Withings提供的显示面板快照。 |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | +该图没问题,但我们都发现它让人困惑,并且对追踪趋势并没有太大的帮助。我还希望能够使用预测算法来基于过去预测未来。 |
| 28 | + |
| 29 | +下面是我们如何构建自己的健康显示面板来取而代之的。 |
| 30 | + |
| 31 | +## Python, API, 和绘图 |
| 32 | + |
| 33 | +我建立了一个基本的[Flask](http://flask.pocoo.org/)应用来从Withings API抽取血压数据,处理数据,并在客户端绘制它。要访问那些数据,我使用了一个[Withings Python库](https://github.com/maximebf/python-withings) (可通过[PyPi](https://pypi.python.org/pypi/withings/0.3)获得)。对于绘制,我选择[Plot.ly](https://plot.ly/)。仅需几行HTML代码,你就能快速的创建功能强大的图。 |
| 34 | + |
| 35 | +第一项任务是从Withings获取原始数据。使用Python库让这个任务变得非常简单。有点棘手的是将获取的数据转换成一些Plotly可以用来绘制的数据。将[Jinja2](http://jinja.pocoo.org/docs/dev/)作为Flask的一部分使用,我找到了一种简单的方法来构建一个文本字符串,用来在模板中渲染。 |
| 36 | + |
| 37 | +我们将定义自己的函数来从Withings API抽取数据 (为了简洁起见,我移除了一些代码,但是[这个repo有你开始所需的一切](https://github.com/bassdread/AlgorithmiaWithWithings) )。我们调用Withings API来获取测量数据,然后遍历响应,从而对测量数据和时间进行排序。我们将同时构建一个用于绘制的对象,以及可以传递给Algorithmia以运行他们的预测算法的原始数据之一。 |
| 38 | + |
| 39 | +```python |
| 40 | +def _fetch_withings(): |
| 41 | + |
| 42 | + results = [] |
| 43 | + client = WithingsApi(creds) |
| 44 | + |
| 45 | + readings = { |
| 46 | + # analysis of past readings |
| 47 | + 'past': { |
| 48 | + 'x': '', |
| 49 | + 'diastolic': '', |
| 50 | + 'systolic': '', |
| 51 | + 'pulse': '', |
| 52 | + 'simple_moving_average' : { |
| 53 | + 'diastolic': '', |
| 54 | + 'pulse': '', |
| 55 | + 'systolic': '', |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + measures = client.get_measures() |
| 61 | + |
| 62 | + last_reading_date = measures[-1].date |
| 63 | + counter = 1 |
| 64 | + |
| 65 | + raw_readings = { |
| 66 | + 'systolic': [], |
| 67 | + 'diastolic': [], |
| 68 | + 'pulse': [], |
| 69 | + } |
| 70 | + |
| 71 | + for measure in measures: |
| 72 | + |
| 73 | + if measure.systolic_blood_pressure\ |
| 74 | + and measure.diastolic_blood_pressure: |
| 75 | + |
| 76 | + next_date = last_reading_date + timedelta(days=counter) |
| 77 | + |
| 78 | + # sort out date times |
| 79 | + readings['past']['x'] += '"' + measure.date.strftime('%Y-%m-%d %H:%M:%S') + '",' |
| 80 | + readings['future']['x'] += '"' + next_date.strftime('%Y-%m-%d %H:%M:%S') + '",' |
| 81 | + |
| 82 | + readings['past']['systolic'] += str(measure.systolic_blood_pressure) + ',' |
| 83 | + readings['past']['diastolic'] += str(measure.diastolic_blood_pressure) + ',' |
| 84 | + |
| 85 | + # keep ints for for sending to ALGORITHMIA |
| 86 | + raw_readings['systolic'].append(measure.systolic_blood_pressure) |
| 87 | + raw_readings['diastolic'].append(measure.diastolic_blood_pressure) |
| 88 | + |
| 89 | + if measure.heart_pulse and measure.heart_pulse > 30: |
| 90 | + raw_readings['pulse'].append(measure.heart_pulse) |
| 91 | + readings['past']['pulse'] += str(measure.heart_pulse) + ',' |
| 92 | + |
| 93 | + counter += 1 |
| 94 | + |
| 95 | + return readings |
| 96 | +``` |
| 97 | + |
| 98 | +与大多数简单的项目相同,[Bootstrap](http://getbootstrap.com/)是用嵌入到正常的行布局的图渲染HTML的完美工具。 |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | +为了构建图表,在抽取Withings数据时,在我们的Flask应用中,以以下格式创建一个对象: |
| 103 | + |
| 104 | +```python |
| 105 | +readings = { |
| 106 | + # predictions |
| 107 | + 'future' : { |
| 108 | + 'x': '', |
| 109 | + 'diastolic': '', |
| 110 | + 'systolic': '', |
| 111 | + 'pulse': '', |
| 112 | + 'simple_moving_average' : { |
| 113 | + 'diastolic': '', |
| 114 | + 'pulse': '', |
| 115 | + 'systolic': '', |
| 116 | + } |
| 117 | + }, |
| 118 | + # analysis of past readings |
| 119 | + 'past': { |
| 120 | + 'x': '', |
| 121 | + 'diastolic': '', |
| 122 | + 'systolic': '', |
| 123 | + 'pulse': '', |
| 124 | + 'simple_moving_average' : { |
| 125 | + 'diastolic': '', |
| 126 | + 'pulse': '', |
| 127 | + 'systolic': '', |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | +``` |
| 132 | + |
| 133 | +然后,要生成图,我们将数据中的x和y坐标传递给Plot.ly 。我们将x当做索引,将y当成像这样的舒张压值、收缩压值或者脉冲值: |
| 134 | + |
| 135 | +```python |
| 136 | +HISTORIC_FUTURE = document.getElementById('historic_future_graph'); |
| 137 | + Plotly.plot(HISTORIC_FUTURE, [ |
| 138 | + { |
| 139 | + name: 'Systolic', |
| 140 | + x: [{{readings.past.x|safe}}], |
| 141 | + y: [{{readings.past.systolic|safe}}] |
| 142 | + }, |
| 143 | + { |
| 144 | + name: 'Diastolic', |
| 145 | + x: [{{readings.past.x|safe}}], |
| 146 | + y: [{{readings.past.diastolic|safe}}] |
| 147 | + }, |
| 148 | + { |
| 149 | + name: 'Pulse', |
| 150 | + x: [{{readings.past.x|safe}}], |
| 151 | + y: [{{readings.past.pulse|safe}}] |
| 152 | + }, |
| 153 | + { |
| 154 | + name: 'Systolic Future', |
| 155 | + x: [{{readings.future.x|safe}}], |
| 156 | + y: [{{readings.future.systolic|safe}}] |
| 157 | + }, |
| 158 | + { |
| 159 | + name: 'Diastolic Future', |
| 160 | + x: [{{readings.future.x|safe}}], |
| 161 | + y: [{{readings.future.diastolic|safe}}] |
| 162 | + }, |
| 163 | + { |
| 164 | + name: 'Pulse Future', |
| 165 | + x: [{{readings.future.x|safe}}], |
| 166 | + y: [{{readings.future.pulse|safe}}] |
| 167 | + } |
| 168 | + ], |
| 169 | + { |
| 170 | + margin: { t: 0 } |
| 171 | + }); |
| 172 | +``` |
| 173 | + |
| 174 | +现在,我们有了自己的图,可以看到,血压数据有一些不可预知的峰值,这使得趋势难以发现。过去,我使用R来处理时间序列数据,但从未在Python中使用任何东西。这就是[Algorithmia](http://algorithmia.com/)的用武之地。 |
| 175 | + |
| 176 | +## 添加预测算法 |
| 177 | + |
| 178 | +我需要尽可能容易地让数据有意义。我研究了一些进行机器学习和数据分析的服务。它们大多数仅限于文本分类,昂贵或者不适合作为无服务器的API。 |
| 179 | + |
| 180 | +后来,我发现了Algorithmia,它有大量的作为微服务运行在你的数据之上的算法库。你负责调用算法,传递数据,而它们会在这之上运行算法,然后实时返回结果。它们有一个[Python库](https://pypi.python.org/pypi/algorithmia/1.0.0),并且由于它是一个API,因此与无服务器项目完美契合。 |
| 181 | + |
| 182 | +对于这个项目,我选择了两个预测算法: |
| 183 | + |
| 184 | + * [简单移动平均](https://algorithmia.com/algorithms/TimeSeries/SimpleMovingAverage) |
| 185 | + * [预测](https://algorithmia.com/algorithms/TimeSeries/Forecast) |
| 186 | + |
| 187 | +### 使用 |
| 188 | + |
| 189 | +我使用[简单移动平均](https://algorithmia.com/algorithms/TimeSeries/SimpleMovingAverage)来平滑数据,使得它更容易在图中绘制趋势。使用移动平均还有助于减少原始数据中的波动和噪音。 |
| 190 | + |
| 191 | +首先,定义我们的简单移动平均函数: |
| 192 | + |
| 193 | +```python |
| 194 | +def _get_simple_moving_average(data): |
| 195 | + |
| 196 | + string = '' |
| 197 | + raw = [] |
| 198 | + |
| 199 | + reply = SIMPLE_MOVING_AVERAGE.pipe(data) |
| 200 | + for reading in reply.result: |
| 201 | + string += str(int(reading)) + ',' |
| 202 | + raw.append(reading) |
| 203 | + |
| 204 | + string = string[:-1] |
| 205 | + |
| 206 | + return string, raw |
| 207 | +``` |
| 208 | + |
| 209 | +然后,作为上面**_fetch_withings()**的一部分,我们像这样传递收缩压数据、舒张压数据和脉冲数据给它: |
| 210 | + |
| 211 | +```python |
| 212 | + # simple moving average of existing data |
| 213 | + readings['past']['simple_moving_average']['diastolic'], average_diastolic = _get_simple_moving_average(raw_readings['diastolic']) |
| 214 | + readings['past']['simple_moving_average']['systolic'], average_systolic = _get_simple_moving_average(raw_readings['systolic']) |
| 215 | + readings['past']['simple_moving_average']['pulse'], average_pulse = _get_simple_moving_average(raw_readings['pulse']) |
| 216 | +``` |
| 217 | + |
| 218 | +这创建了一个生命体征数据的理顺图。然后,使用预测算法来预测未来趋势。 |
| 219 | + |
| 220 | +### 使用预测 |
| 221 | + |
| 222 | +这就是事情变得很有趣的地方。我手上有差不多五个月的数据,而且一般来说,数据越多越好! |
| 223 | + |
| 224 | +首先,定义我们的预测函数: |
| 225 | + |
| 226 | +```python |
| 227 | +def _get_forecast(data): |
| 228 | + |
| 229 | + string = '' |
| 230 | + raw = [] |
| 231 | + |
| 232 | + reply = FORECAST.pipe(data) |
| 233 | + for reading in reply.result: |
| 234 | + string += str(int(reading)) + ',' |
| 235 | + raw.append(reading) |
| 236 | + |
| 237 | + string = string[:-1] |
| 238 | + |
| 239 | + return string, raw |
| 240 | +``` |
| 241 | + |
| 242 | +此外,作为**_fetch_withings()**函数的一部分,我将数据作为数组传递给该预测函数。我们可以停在这里,但预测的数据容易发生尖峰和波动。因此,一旦完成,则在预测数据上运行移动平均算法,从而平滑结果: |
| 243 | + |
| 244 | +```python |
| 245 | +if FORECAST_ON_AVERAGE: |
| 246 | + readings['future']['diastolic'], future_diastolic = _get_forecast(average_diastolic) |
| 247 | + readings['future']['systolic'], future_systolic = _get_forecast(average_systolic) |
| 248 | + readings['future']['pulse'], future_pulse = _get_forecast(average_pulse) |
| 249 | + else: |
| 250 | + # populate the standard graphs and get the raw data to feed into thenext algorithm |
| 251 | + readings['future']['diastolic'], future_diastolic = _get_forecast(raw_readings['diastolic']) |
| 252 | + readings['future']['systolic'], future_systolic = _get_forecast(raw_readings['systolic']) |
| 253 | + readings['future']['pulse'], future_pulse = _get_forecast(raw_readings['pulse']) |
| 254 | + |
| 255 | + # simple moving average of future data |
| 256 | + readings['future']['simple_moving_average']['diastolic'], average_diastolic = _get_simple_moving_average(future_diastolic) |
| 257 | + readings['future']['simple_moving_average']['systolic'], average_systolic = _get_simple_moving_average(future_systolic) |
| 258 | + readings['future']['simple_moving_average']['pulse'], average_pulse = _get_simple_moving_average(future_pulse) |
| 259 | +``` |
| 260 | + |
| 261 | +下面的绘制结果,显示了五个月的收缩压、舒张压和脉冲数据,每个都带有一个五个月的预测: |
| 262 | + |
| 263 | + |
| 264 | + |
| 265 | +这里是预测算法的输出,但这次,使用简单移动平均数据来取代原始数据: |
| 266 | + |
| 267 | + |
| 268 | + |
| 269 | +好多了!血压数据很难处理,因为有时它可能非常不稳定。有大量的用于平滑和标准化数据的算法,以后,我打算用它们来改善预测。例如,我可以用[线性校正](https://algorithmia.com/algorithms/TimeSeries/LinearDetrend)来专注于数据中的波动分析,或者用[自动校正](https://algorithmia.com/algorithms/TimeSeries/Autocorrelate)来分析时间序列的季节性。我甚至可以使用[异常检测](https://algorithmia.com/algorithms/TimeSeries/OutlierDetection)来移除原始数据中的异常数据点,它们会表明糟糕的读数。 |
| 270 | + |
| 271 | +## 总结 |
| 272 | + |
| 273 | +最主要的是,它表明,我朋友的血压不会变得更糟,并且在接下来的几个月中,应该保持在可接受的范围内。 |
| 274 | + |
| 275 | +而且,多亏了他们的新健康显示面板,我的朋友现在有了一组在讨论长期治疗时可以带给他们的医生的图。血压是某些会被一系列因素影响的东西,因此对于长期管理,定期审查是重要的。 |
| 276 | + |
| 277 | +这是我在进行预测和了解这类型数据处理背后许许多多想法的第一次尝试。我仅仅触及那些可以用数据来完成的服务。 |
| 278 | + |
| 279 | +使用的工具: |
| 280 | + |
| 281 | + * [Flask Python框架](http://flask.pocoo.org/) |
| 282 | + * [Algorithmia](http://algorithmia.com/) |
| 283 | + * [Plotly](http://plot.ly/) |
| 284 | + |
0 commit comments