Upgrade to Pro — share decks privately, control downloads, hide ads and more …

すべてのヘルスケアデータを紐解く.pdf

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for coe coe
August 24, 2024

 すべてのヘルスケアデータを紐解く.pdf

Avatar for coe

coe

August 24, 2024
Tweet

More Decks by coe

Other Decks in Technology

Transcript

  1. GPXͷத਎ <?xml version="1.0" encoding="utf-8"?> <gpx version="1.1" creator="apple health export" xmlns="http://www.topografix.com/gpx/1/1"

    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http:// www.topografix.com/gpx/1/1 http://www.topografix.com/gpx/1/1/gpx.xsd"> <metadata> <time>2024-08-11t10:11:58z</time> </metadata> <trk> <name>route 2022-03-09 7:53pm</name> <trkseg> <trkpt lon="139.688067" lat="35.754748"><ele>28.710350</ele><time>2022-03-09t10:53:35z</time><extensions><speed>0.000000</speed><course>92.615112</ course><hacc>15.598062</hacc><vacc>7.818013</vacc></extensions></trkpt> </trkseg> </trk> </gpx>
  2. trkptλά಺ίϯςϯπ ໊લ ஋ ஋ͷྫ උߟ lat Ң౓ 35.685067 trksegଐੑ lon

    ܦ౓ 139.729145 trksegଐੑ ele ඪߴ 35.618244 time ه࿥͞Εͨ೔࣌ 2018-09-19T03:08 :01Z speed Ҡಈ଎౓(m/s) 1.623222 Apple extensions course Ҡಈํ޲(°) 186.328125 Apple extensions hAcc ਫฏํ޲ͷਫ਼౓ 2.350130 Apple extensions vAcc ਨ௚ํ޲ͷਫ਼౓ 1.664492 Apple extensions
  3. MacOS XMLDocumentʹΑΔGPXͷղੳྫ func loadGPXData() { guard let gpxPath = Bundle.main.url(forResource:

    "route_2021-09-24_6.30pm", withExtension: "gpx"), let xmlDoc = try? XMLDocument(contentsOf: gpxPath, options: .documentTidyXML) else { return } let xpath = "//gpx/trk/trkseg/trkpt" let trkptNodes = try! xmlDoc.nodes(forXPath: xpath) var coordinates: [CLLocationCoordinate2D] = [] trkptNodes.forEach { node in if let element = node as? XMLElement, let latStr = element.attribute(forName: "lat")?.stringValue, let lonStr = element.attribute(forName: "lon")?.stringValue, let lat = CLLocationDegrees(latStr), let lon = CLLocationDegrees(lonStr) { let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon) coordinates.append(coordinate) } } }
  4. ECG csv ͷswift data struct ECGData { var name: String

    var dateOfBirth: String var recordedDate: String var classification: String var symptoms: String? var softwareVersion: String var device: String var sampleRate: Double var lead: String var unit: String var voltage: [Double] }
  5. ECG csv ͷswiftॲ ཧྫ // CSVϑΝΠϧͷ಺༰Λύʔεͯ͠Structʹม׵͢Δؔ਺ func parseECGCSV(csvString: String) ->

    ECGData { let rows = csvString.components(separatedBy: "\n") let name = splitCSVLine(rows[0])[1] let dateOfBirth = splitCSVLine(rows[1])[1] let recordedDate = splitCSVLine(rows[2])[1] let classification = splitCSVLine(rows[3])[1] let symptoms = splitCSVLine(rows[4]).count > 1 ? splitCSVLine(rows[4])[1] : nil let softwareVersion = splitCSVLine(rows[5])[1] let device = splitCSVLine(rows[6])[1] let sampleRate = Double(splitCSVLine(rows[7])[1].components(separatedBy: " ")[0])! let lead = splitCSVLine(rows[10])[1] let unit = splitCSVLine(rows[11])[1] // ిѹͷऔಘ var signals = [Double]() for i in 13..<rows.count { let columns = splitCSVLine(rows[i]) if let voltage = Double(columns[0]) { signals.append(voltage) } } ...
  6. CSVҰྻ ͷॲཧྫ func splitCSVLine(_ line: String) -> [String] { var

    result = [String]() var currentField = "" var inQuotes = false for character in line { if character == "\"" { inQuotes.toggle() // Ҿ༻ූ͕ݱΕͨΒinQuotesϑϥάΛ੾Γସ͑ } else if character == "," && !inQuotes { result.append(currentField.trimmingCharacters(in: .whitespaces)) currentField = "" } else { currentField.append(character) } } result.append(currentField.trimmingCharacters(in: .whitespaces)) return result }
  7. ڊେͳXMLΛ։͘ • ͦͷ··μϒϧΫϦοΫ o XcodeͰ։͘ ▪ ϑϦʔζ • ςΩετΤσΟοτͰ։͘ o

    ͱΓ͋͑ͣಈ͘ ▪ ͕ͩҰ౓ʹεΫϩʔϧ͢ΔͱϑϦʔζ͢Δ৔߹͕͋Δ
  8. EXPORT.XML ͷDTDʹఆٛ ࡁΈͷཁૉ ཁૉ ಺༰ HealthData root ExportDate ΤΫεϙʔτ೔෇ Me

    ݸਓ৘ใ Record ه࿥͞Ε݈ͨ߁Ϩίʔυ Correlation ه࿥͞Εͨσʔλͷάϧʔ ϓ Workout ϫʔΫΞ΢τ ActivitySummary ΞΫςΟϏςΟৄࡉ MetadataEntry ϝλσʔλ HeartRateVariabilityMetadataList ৺ഥมಈϦετ InstantaneousBeatsPerMinute ৺ഥ਺ ClinicalRecord ྟচه࿥ Audiogram ௌྗݕࠪ SensitivityPoint ௌྗײ౓ VisionPrescription ࢹྗ RightEye ӈ໨ʹؔ͢Δσʔλ LeftEye ࠨ໨ʹؔ͢Δσʔλ Attachment ఴ෇ϑΝΠϧ
  9. EXPORT.XML ͷDTDʹఆٛ ࡁΈͷཁૉ ཁૉ ಺༰ HealthData root ExportDate ΤΫεϙʔτ೔෇ Me

    ݸਓ৘ใ Record ه࿥͞Ε݈ͨ߁Ϩίʔυ Correlation ه࿥͞Εͨσʔλͷάϧʔ ϓ Workout ϫʔΫΞ΢τ ActivitySummary ΞΫςΟϏςΟৄࡉ MetadataEntry ϝλσʔλ HeartRateVariabilityMetadataList ৺ഥมಈϦετ InstantaneousBeatsPerMinute ৺ഥ਺ ClinicalRecord ྟচه࿥ Audiogram ௌྗݕࠪ SensitivityPoint ௌྗײ౓ VisionPrescription ࢹྗ RightEye ӈ໨ʹؔ͢Δσʔλ LeftEye ࠨ໨ʹؔ͢Δσʔλ Attachment ఴ෇ϑΝΠϧ
  10. RECORD <Record type="HKQuantityTypeIdentifierBodyMass" sourceName="omron connect" sourceVersion="006.006.00000.001" unit="kg" creationDate="2021-04-27 07:07:03 +0900"

    startDate="2021-04-27 07:05:58 +0900" endDate="2021-04-27 07:05:58 +0900" value="69.3"> <MetadataEntry key="SequenceNumber" value="0"/> <MetadataEntry key="HKTimeZone" value="Asia/Tokyo"/> <MetadataEntry key="StartDateLocal" value="20210427070558"/> <MetadataEntry key="UserNumber" value="2"/> <MetadataEntry key="HKDeviceName" value="HBF-255T"/> <MetadataEntry key="HKDeviceManufacturerName" value="OMRON HEALTHCARE Co., Ltd."/> </Record>
  11. RECORD <Record type="HKQuantityTypeIdentifierBodyMass" sourceName="omron connect" sourceVersion="006.006.00000.001" unit="kg" creationDate="2021-04-27 07:07:03 +0900"

    startDate="2021-04-27 07:05:58 +0900" endDate="2021-04-27 07:05:58 +0900" value="69.3"> <MetadataEntry key="SequenceNumber" value="0"/> <MetadataEntry key="HKTimeZone" value="Asia/Tokyo"/> <MetadataEntry key="StartDateLocal" value="20210427070558"/> <MetadataEntry key="UserNumber" value="2"/> <MetadataEntry key="HKDeviceName" value="HBF-255T"/> <MetadataEntry key="HKDeviceManufacturerName" value="OMRON HEALTHCARE Co., Ltd."/> </Record> typeλά͔ΒσʔλΛಛఆ͢Δ
  12. RECORD <Record type="HKQuantityTypeIdentifierBodyMass" sourceName="omron connect" sourceVersion="006.006.00000.001" unit="kg" creationDate="2021-04-27 07:07:03 +0900"

    startDate="2021-04-27 07:05:58 +0900" endDate="2021-04-27 07:05:58 +0900" value="69.3"> <MetadataEntry key="SequenceNumber" value="0"/> <MetadataEntry key="HKTimeZone" value="Asia/Tokyo"/> <MetadataEntry key="StartDateLocal" value="20210427070558"/> <MetadataEntry key="UserNumber" value="2"/> <MetadataEntry key="HKDeviceName" value="HBF-255T"/> <MetadataEntry key="HKDeviceManufacturerName" value="OMRON HEALTHCARE Co., Ltd."/> </Record> RECORDλά͔ΒσʔλΛऔಘ
  13. correlation <Correlation type="HKCorrelationTypeIdentifierFood" sourceName="YAZIO" sourceVersion="1833" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31

    +0900" endDate="2024-08-10 20:35:31 +0900"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> <Record type="HKQuantityTypeIdentifierDietaryEnergyConsumed" sourceName="YAZIO" sourceVersion="1833" unit="kcal" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="83"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> <Record type="HKQuantityTypeIdentifierDietaryFatTotal" sourceName="YAZIO" sourceVersion="1833" unit="g" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="5.5"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> <Record type="HKQuantityTypeIdentifierDietaryProtein" sourceName="YAZIO" sourceVersion="1833" unit="g" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="7.10001"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> <Record type="HKQuantityTypeIdentifierDietaryCarbohydrates" sourceName="YAZIO" sourceVersion="1833" unit="g" creationDate="2024-08- 10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="0.170005"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> </Correlation>
  14. correlation 
 ৯΂෺ͱӫཆૉ ͷσʔλ <Correlation type="HKCorrelationTypeIdentifierFood" sourceName="YAZIO" sourceVersion="1833" creationDate="2024-08-10 20:35:31

    +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> <Record type="HKQuantityTypeIdentifierDietaryEnergyConsumed" sourceName="YAZIO" sourceVersion="1833" unit="kcal" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="83"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> <Record type="HKQuantityTypeIdentifierDietaryFatTotal" sourceName="YAZIO" sourceVersion="1833" unit="g" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="5.5"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> <Record type="HKQuantityTypeIdentifierDietaryProtein" sourceName="YAZIO" sourceVersion="1833" unit="g" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="7.10001"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> <Record type="HKQuantityTypeIdentifierDietaryCarbohydrates" sourceName="YAZIO" sourceVersion="1833" unit="g" creationDate="2024-08-10 20:35:31 +0900" startDate="2024-08-10 20:35:31 +0900" endDate="2024-08-10 20:35:31 +0900" value="0.170005"> <MetadataEntry key="HKExternalUUID" value="040f43f9-08dd-4876-9d01-8893cf17285d"/> <MetadataEntry key="HKFoodType" value="ཛ"/> </Record> </Correlation>
  15. XMLParserʹΑΔ SAXॲཧྫ class HealthDataParser: NSObject, XMLParserDelegate { private var bodyMassRecords:

    [[String: String]] = [] private var isParsingRecord = false func parseHealthData(contents: URL) -> [[String: String]] { let parser = XMLParser(contentsOf: contents) parser?.delegate = self parser?.parse() return bodyMassRecords } func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { if elementName == "Record" && attributeDict["type"] == "HKQuantityTypeIdentifierBodyMass" { isParsingRecord = true if attributeDict.contains(where: { key, value in key == "value" && Double(value)! < 70 }) { bodyMassRecords.append(attributeDict) if bodyMassRecords.count > 20 { parser.abortParsing() } } } } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == "Record" { isParsingRecord = false } } } • XMLParserΛϩʔΧϧϑΝΠϧURLύ ε(contentsOf:)Ͱੜ੒ • ඞཁͳऔಘର৅ͷσʔλ(record)ͷॲཧ Λߦ͏ • औಘ͠ऴΘͬͨΒ parser.abortParsing()Λݺͼग़ͯ͠ XMLͷಡΈࠐΈΛΩϟϯηϧ͢Δ
  16. CDA 
 ʢCLINICAL DOCUMENT ARCHITECTUREʣ • ҩྍ৘ใͷඪ४ن֨ o HL7(HEALTH LEVEL

    7)͕։ൃ • ҩྍ৘ใΛඪ४Խ͞Εͨํ๏Ͱిࢠతʹަ׵͢Δ͜ͱ • จॻ͸XMLܗࣜͰදݱ͞Ε͍ͯΔ o ϔομʔ o ྟচ৘ใ(COMPONENT)
  17. CDAͷຊ จ(ױऀ৘ ใ) <recordTarget> <patientRole> <id root="2.16.840.1.113883.4.6" nullFlavor="NA"/> <patient> <name

    use="CL">೔޲ڧ</name> <administrativeGenderCode code="M" codeSystem="2.16.840.1.113883.5.1" displayName="Male"/> <birthTime value="19800331"/> </patient> </patientRole> </recordTarget>
  18. CDAͷຊ จ(ྟচ৘ ใ) <COMPONENT> <OBSERVATION CLASSCODE="OBS" MOODCODE="EVN"> <TEMPLATEID ROOT="2.16.840.1.113883.10.20.22.4.27"/> <ID

    ROOT="C6F88321-67AD-11DB-BD13-0800200C9A66"/> <CODE CODE="3141-9" CODESYSTEM="2.16.840.1.113883.6.1" CODESYSTEMNAME="LOINC" DISPLAYNAME="BODY WEIGHT MEASURED"/> <TEXT> <SOURCENAME>OMRON CONNECT</SOURCENAME> <SOURCEVERSION>006.006.00000.001</SOURCEVERSION> <VALUE>69.3</VALUE> <TYPE>HKQUANTITYTYPEIDENTIFIERBODYMASS</TYPE> <UNIT>KG</UNIT> <METADATAENTRY> <KEY>SEQUENCENUMBER</KEY> <VALUE>0</VALUE> </METADATAENTRY> </TEXT> <STATUSCODE CODE="COMPLETED"/> <EFFECTIVETIME> <LOW VALUE="20210427070558+0900"/> <HIGH VALUE="20210427070558+0900"/> </EFFECTIVETIME> <VALUE XSI:TYPE="PQ" VALUE="69.3" UNIT="KG"/> <INTERPRETATIONCODE CODE="N" CODESYSTEM="2.16.840.1.113883.5.83"/> </OBSERVATION> </COMPONENT>
  19. CDAͷຊ จ(ྟচ৘ ใ) <COMPONENT> <OBSERVATION CLASSCODE="OBS" MOODCODE="EVN"> <TEMPLATEID ROOT="2.16.840.1.113883.10.20.22.4.27"/> <ID

    ROOT="C6F88321-67AD-11DB-BD13-0800200C9A66"/> <CODE CODE="3141-9" CODESYSTEM="2.16.840.1.113883.6.1" CODESYSTEMNAME="LOINC" DISPLAYNAME="BODY WEIGHT MEASURED"/> <TEXT> <SOURCENAME>OMRON CONNECT</SOURCENAME> <SOURCEVERSION>006.006.00000.001</SOURCEVERSION> <VALUE>69.3</VALUE> <TYPE>HKQUANTITYTYPEIDENTIFIERBODYMASS</TYPE> <UNIT>KG</UNIT> <METADATAENTRY> <KEY>SEQUENCENUMBER</KEY> <VALUE>0</VALUE> </METADATAENTRY> </TEXT> <STATUSCODE CODE="COMPLETED"/> <EFFECTIVETIME> <LOW VALUE="20210427070558+0900"/> <HIGH VALUE="20210427070558+0900"/> </EFFECTIVETIME> <VALUE XSI:TYPE="PQ" VALUE="69.3" UNIT="KG"/> <INTERPRETATIONCODE CODE="N" CODESYSTEM="2.16.840.1.113883.5.83"/> </OBSERVATION> </COMPONENT>
  20. LOINC 
 ʢLOGICAL OBSERVATION IDENTIFIERS NAMES AND CODESʣ • ҩྍݕࠪ݁Ռ΍ྟচ؍࡯Λࣝผ͢ΔͨΊͷσʔλϕʔε͓Αͼੈքڞ௨ͷඪ४ن֨

    • ֤ݕࠪ΍؍࡯݁Ռʹݻ༗ͷ7ܻͷLONICίʔυׂ͕Γ౰ͯΒΕΔ o ίʔυʹΑͬͯྟচݕࠪ΍਍ྍه࿥ͷ಺༰Λॲཧ͢Δ • CodeSystem 2.16.840.1.113883.6.1
  21. COMPONENT ͔Β஋Λऔಘ <component> <observation classCode="OBS" moodCode="EVN"> <templateId root="2.16.840.1.113883.10.20.22.4.27"/> <id root="c6f88321-67ad-11db-bd13-0800200c9a66"/>

    <code code="3141-9" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC" displayName="Body weight Measured"/> <text> <sourceName>omron connect</sourceName> <sourceVersion>006.006.00000.001</sourceVersion> <value>69.3</value> <type>HKQuantityTypeIdentifierBodyMass</type> <unit>kg</unit> <metadataEntry> <key>SequenceNumber</key> <value>0</value> </metadataEntry> </text> <statusCode code="completed"/> <effectiveTime> <low value="20210427070558+0900"/> <high value="20210427070558+0900"/> </effectiveTime> <value xsi:type="PQ" value="69.3" unit="kg"/> <interpretationCode code="N" codeSystem="2.16.840.1.113883.5.83"/> </observation> </component> • codeλά 3141-9ͰମॏΛ͍ࣔͯ͠Δ͜ ͱ͕Θ͔Δ • Valueλά(xsi:type)ʹvalue(஋)ͱ unit(୯Ґ) o iOS͕ੜ੒͢Δσʔλʹvalueλά͸ෳ ਺͋Δ৔߹΋͋Δ
  22. EXPORT XMLͷҧ͍ • export.xml o HealthKitΛج४ͱͨ͠σʔλ͕ग़ྗ͞Ε Δ o ղੳʹ͸HealthKitͷ஌ࣝ΍ॲཧ͕ඞཁʹ ͳΔ

    • export_cda.xml o ඪ४ن֨ʹԊͬͨσʔλ͕ग़ྗ͞ΕΔ o ӫཆͳͲඪ४ن֨ʹແ͍σʔλ͸ग़ྗ͞ Εͳ͍
  23. ·ͱΊ • iPhone͔ΒϔϧεέΞσʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ • workout-routesͱͯ͠ϫʔΫΞ΢τϧʔτΛࣔ͢gpxϑΝΠϧ͕औಘͰ͖Δ • electrocardiogramsͱͯ͠৺ిਤ͕csvϑΝΠϧͰऔಘͰ͖Δ o ҰͭͷCSVϑΝΠϧʹϝλσʔλ෦෼ͱిѹ෦෼ʹ෼͔Ε͍ͯΔ o

    σʔλ͸୺຤ͷݴޠઃఆʹӨڹ͞ΕΔ • xmlϑΝΠϧ͔ΒϔϧεέΞσʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ o export.xml͔Β͸HealthKitʹ४ڌͨ͠σʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ o export_cda.xml͔Β͸ඪ४ن֨ͷ݈߁σʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ ▪ xml͸ڊେʹͳ͍ͬͯΔͷͰSAXͰύʔε