-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.Rmd
329 lines (252 loc) · 9.84 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
---
output: github_document
editor_options:
chunk_output_type: console
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
options(width = 300)
```
# HL7.R <img src='man/figures/logo.png' align="right" height="139" />
<!-- badges: start -->
[![R-CMD-check](https://github.com/Shaunson26/HL7.R/actions/workflows/r-cmd-check.yml/badge.svg)](https://github.com/Shaunson26/HL7.R/actions/workflows/r-cmd-check.yml)
![GitHub R package version](https://img.shields.io/github/r-package/v/shaunson26/HL7.R)
<!-- badges: end -->
The goal of HL7.R is to enable wrangling of HL7 2.3.1/2.4 in R. This package was
driven by the need of HL7 wrangling for NCIMS tasks (a notifiable conditions database for
a state health authority). HL7 messages are just flattened list objects, and as such
can be imported into R as a nested list.
Information on HL7 specifications were sourced from:
- http://www.hl7.eu/HL7v2x/v231/hl7v231segm.htm
- https://hl7-definition.caristix.com/v2/
## Installation
``` r
# install.packages("devtools")
devtools::install_github("Shaunson26/HL7.R")
```
```{r, echo=F}
library(devtools)
load_all()
```
## Parsing a HL7
The first problem this package solves is parsing of HL7 messages into a nested list. The
function `parse_hl7_message()` will parse a HL7 file into a list. This works for both
single messages and batch messages. For each parsed message, the function will try it's
best at naming all the elements. This is simple for the top level elements (HL7 segments),
but a bit more cumbersome within the list elements (HL7 segment fields). By convention, fields are
referenced by number ("The fourth field in MSH is the sending facility"), but
having names can make things a bit more readable at times. Repeated segments within a
message are numbered in the output list e.g. OBX.1 OBX.2 (using the Set ID value).
**Notes**
- All values should be imported as text, and any conversion needs to be done downstream e.g with datetimes
- A `filename` attribute is attached with the list object
### Single HL7 message
Use `parse_hl7_message()` on a file path.
```{r}
hl7_file <- system.file(package = 'HL7.R', 'extdata/hl7-2.3.1.hl7')
hl7_list <- parse_hl7_message(hl7_file)
names(hl7_list)
# First 5 fields of these segments
hl7_list$MSH[1:5]
hl7_list$PID[1:5]
```
```{r}
# File from where data parsed
attr(hl7_list, 'filename')
```
Traditionally indexes are used, but using named elements can help readability.
```{r}
with(hl7_list,
data.frame(
first_name = PID[[5]][[2]],
last_name = PID[[5]][[1]],
suburb = PID[[11]][[3]]
)
)
with(hl7_list,
data.frame(
first_name = PID$PatientName$givenName,
last_name = PID$PatientName$familyName,
suburb = PID$PatientAddress$city
)
)
```
### Batch HL7 message
`parse_hl7_message()` will check for batch headers and parse appropriately. The
result is a list of parsed messages.
```{r}
hl7_file <- system.file(package = 'HL7.R', 'extdata/fake-covid-batch.hl7')
hl7_list <- parse_hl7_message(hl7_file)
# unnamed list of length of the number of messages
length(hl7_list)
names(hl7_list)
# a single message lives within the list elements now
names(hl7_list[[1]])
# accessing things
hl7_list[[1]]$PID$PatientName
hl7_list[[2]]$PID$PatientName
```
### Multiple HL7 to line list
Cycle through each file and use `parse_hl7_message()`
```{r}
# Two HL7 files starting with 'fake' are distributed with this package,
hl7_files <-
system.file(package = 'HL7.R', 'extdata') %>%
list.files(pattern = 'fake-covid-\\d.hl7$', full.names = T)
basename(hl7_files)
# Parse into list
hl7_list <- lapply(hl7_files, parse_hl7_message)
length(hl7_list)
# Equivalent result to a parsed batch HL7
hl7_list[[1]]$PID$PatientName
hl7_list[[2]]$PID$PatientName
# el = list element in each loop
lapply(hl7_list, function(el){
with(el,
data.frame(
first_name = PID$PatientName$givenName,
last_name = PID$PatientName$familyName,
suburb = PID$PatientAddress$city,
lab = MSH$SendingFacility,
test = OBX.1$ObservationIdentifier$text,
result = OBX.1$ObservationValue[[2]]
)
)
}) %>%
do.call(rbind.data.frame, .)
```
## Creating HL7 2.3.1
The second problem this package solves is converting any arbitrary piece of data into a HL7 message.
This is conducted by piecing together _*segments*_ such as the message header (MSH), Patient identification (PID)
and Observation/Results (OBX). This package provides segment functions e.g. `MSH()`, `PID()`, with named parameters that will
build a text segment ready for piecing together using the function `build_hl7()`.
**Notes**
- There a default blank values for all the fields in a segment function, and they will be included
in the final output as shown below up until the last observed value i.e. trailing blanks are trimmed
see the parameter `.trim` in most functions
- There are helper functions `*Components()` for nested fields. As these are helpers, you can safely skip
if you can correctly create the required value
- Dates/Datetimes can be converted using `datetime_to_hl7_datetime()`
```{r}
example_hl7_build <-
build_hl7(
MSH(SendingFacility = 'A lab', ReceivingFacility = 'NSW Health', VersionID = '2.3.1'),
PID(PatientID = '1234',
PatientName = PatientNameComponents(familyName = 'Ross',
givenName = 'Bob'),
PatientAddress = PatientAddressComponents(streetAddress = '123 Fake Street',
city = 'Springfield',
zipPostcode = '90120')),
OBX(SetID = 1,
ValueType = 'CE',
ObservationIdentifier = ObservationIdentifierComponents(identifier = 'NSW_LOINC-376',
text = 'nCoV-2019 PCR',
nameOfCodingSystem = 'LN'),
ObservationValue = variedComponents('260415000^Not Detected^SNOMED-CT'))
)
```
```{r}
example_hl7_build
```
Conversion of Date and Datetimes
```{r}
datetime_to_hl7_datetime(Sys.time())
```
`.trim` will trim trailing blank fields. It is `TRUE` by default
```{r}
MSH(SendingFacility = 'NSW HEALTH', VersionID = '2.3.1')
MSH(SendingFacility = 'NSW HEALTH', VersionID = '2.3.1', .trim = FALSE)
PatientAddressComponents(streetAddress = '123 Fake Street')
PatientAddressComponents(streetAddress = '123 Fake Street', .trim = FALSE)
```
### Line list to HL7
Often we have the task of translating a line list into HL7 messages. A simple
example is show below:
- loop through the rows using `lapply`
- convert the row into a list for easy reference e.g. `d$firstname`
```{r, echo = F, eval = F}
some_line_list <-
rbind.data.frame(
list(firstname = 'Homer', lastname = 'Simpson',
street = '742 Evergreen Terrace', suburb = 'Springfield',
test_id = 'ncov19', text_text = 'nCoV-2019 PCR',
result_code = 'P', result_text = 'Postive',
facility = 'Dr Hibbert Medical')
,
list(firstname = 'Ned', lastname = 'Flanders',
street = '744 Evergreen Terrace', suburb = 'Springfield',
test_id = 'ncov19', text_text = 'nCoV-2019 PCR',
result_code = 'N', result_text = 'Negative',
facility = 'Nick Riviera Appartment')
)
readr::write_csv(some_line_list, file = 'inst/extdata/fake-covid-n2.csv')
```
```{r}
# A 2 row line list with fake data is distributed with this package
some_line_list <- read.csv(system.file(package = 'HL7.R', 'extdata/fake-covid-n2.csv'))
some_line_list
```
```{r}
hl7_build_list <-
lapply(1:nrow(some_line_list), function(row){
# d = data element (the row, as a list)
d <- as.list(some_line_list[row,])
build_hl7(
MSH(SendingFacility = d$facility, ReceivingFacility = 'NSW Health', VersionID = '2.3.1'),
PID(PatientID = '1',
PatientName = PatientNameComponents(familyName = d$firstname,
givenName = d$lastname),
PatientAddress = PatientAddressComponents(streetAddress = d$street,
city = d$suburb)),
OBX(SetID = 1,
ValueType = 'CE',
ObservationIdentifier = ObservationIdentifierComponents(identifier = d$test_id,
text = d$text_text,
nameOfCodingSystem = 'LN'),
ObservationValue = variedComponents(d$result_code, d$result_text))
)
})
```
```{r}
hl7_build_list
```
```{r, eval = F}
# for filenames 001, 002, etc if necessary
n_leading_zero <- sprintf('%%0%sd', nchar(length(hl7_build_list)))
# Output somewhere
# * note this was used to make fake-covid-*.hl7 used above
for(i in seq_along(hl7_build_list)){
i_leading_zero = sprintf(fmt = n_leading_zero, i)
filename = sprintf('Some-filename-%s.hl7', i_leading_zero)
path = file.path('some/path', filename)
writeLines(hl7_build_list[[i]], con = path)
}
```
## Installation other
### Devops
Clone repo, open `HL7.R.Rproj` and use `devtools`
```{r}
library(devtools)
load_all()
```
### From archive
Someone built the package and has given you the file - be aware if they built using
source or binary. Generally, `tar.gz` = source, `zip` = binary
```{r, eval = F}
install.packages('HL7.R.xxx.tar.gz', repos = NULL, type = 'source')
install.packages('HL7.R.xxx.zip', repos = NULL)
```
### From DRAT
A local package repo may exist. Again, be aware if they built using source or binary.
Look for `./bin` and `./src` in the DRAT folder.
```{r, eval = F}
library(drat)
drat::addRepo("workgroup", 'file:drive:/path/to/drat')
install.packages('HL7.R', repos = options()$repos[2]) # assuming binary
```