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

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

coe
August 24, 2024

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

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Ͱύʔε