Utiliser l'API Cloud Healthcare pour anonymiser des données cliniques FHIR


Ce tutoriel explique aux chercheurs, aux data scientists et aux équipes informatiques travaillant avec des organisations du secteur de la santé et des sciences de la vie comment utiliser les opérations d'anonymisation des ressources FHIR (Fast Healthcare Interoperability Resources) de l'API Cloud Healthcare pour supprimer ou modifier des informations personnelles, y compris les données de santé protégées, des données cliniques FHIR. L'anonymisation des données médicales contribue à protéger la confidentialité des patients et à préparer les données de santé en vue de leur utilisation dans le cadre de travaux de recherche, du partage de données et du machine learning.

Dans ce tutoriel, nous partons du principe que vous disposez de connaissances de base sur Linux. Des connaissances de base sur Google Cloud ainsi que sur la spécification FHIR et son utilisation dans les systèmes de dossiers médicaux électroniques (EHR) sont également utiles. Exécutez toutes les commandes de ce tutoriel dans Cloud Shell.

Objectifs

  • Créer un dataset et un magasin FHIR pour l'API Cloud Healthcare
  • Importer des données FHIR dans le magasin FHIR de l'API Cloud Healthcare
  • Utiliser l'opération d'anonymisation FHIR de l'API Cloud Healthcare pour supprimer ou modifier les informations personnelles et les données de santé protégées dans les instances FHIR d'un magasin FHIR
  • Utiliser l'outil de ligne de commande curl pour effectuer un appel d'anonymisation FHIR via l'API Cloud Healthcare

Coûts

Ce tutoriel utilise les composants facturables Google Cloud suivants :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût.

Avant de commencer

Toute utilisation de l'API Cloud Healthcare s'effectue dans le cadre d'un projet Google Cloud. Les projets constituent une base pour la création, l'activation et l'utilisation de tous les services Google Cloud, comme la gestion des API, la facturation, l'ajout et la suppression de collaborateurs et la gestion des autorisations pour les ressources Google Cloud. Utilisez la procédure suivante pour créer un projet Google Cloud ou sélectionnez un projet que vous avez déjà créé.

  1. In the Google Cloud console, go to the project selector page.

    Go to project selector

  2. Select or create a Google Cloud project.

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Cloud Healthcare API.

    Enable the API

  5. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  6. Dans Cloud Shell, exécutez la commande gcloud components update pour vous assurer que vous disposez de la dernière version de l'outil incluant les fonctionnalités de l'API Cloud Healthcare.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Créer un compte de service IAM

Les rôles Administrateur d'ensembles de données Healthcare, Administrateur FHIR et Éditeur de ressources FHIR sont requis pour ce tutoriel. Pour créer un compte de service et attribuer les rôles appropriés, procédez comme suit :

  1. Créez un compte de service.
  2. Attribuez des rôles au compte de service :

    • Administrateur d'ensembles de données Healthcare
    • Administrateur Healthcare FHIR
    • Éditeur de ressources FHIR Healthcare
  3. Créez et téléchargez la clé JSON du compte de service.

  4. Activez la clé de votre compte de service :

    gcloud auth activate-service-account --key-file=path-to-key-file
    

    Le résultat est le suivant :

    Activated service account credentials for: [key-name@project-name.iam.gserviceaccount.com]
    
    • key-name est le nom que vous avez attribué à la clé du compte de service.
    • project-name est le nom de votre projet Google Cloud.

Obtenir un jeton d'accès OAuth 2.0

Pour ingérer des données à l'aide de l'API Cloud Healthcare, vous devez disposer d'un jeton d'accès OAuth 2.0 que les commandes de ce tutoriel vous permettent d'obtenir. Dans ce tutoriel, certains exemples de requêtes API Cloud Healthcare utilisent l'outil de ligne de commande curl. Ces exemples font appel à la commande gcloud auth print-access-token pour obtenir un jeton de support OAuth 2.0 et pour inclure ce jeton dans l'en-tête d'autorisation de la requête. Pour en savoir plus sur cette commande, consultez la page sur gcloud auth application-default print-access-token.

Configurer l'ensemble de données FHIR pour l'anonymisation

Chaque ressource FHIR est un objet de type JSON contenant des paires clé/valeur. Certains éléments sont standardisés, d'autres sont en texte libre. Vous pouvez utiliser l'opération d'anonymisation pour les opérations suivantes :

  • Supprimer les valeurs de clés spécifiques dans la ressource FHIR
  • Traiter le texte non structuré pour ne supprimer que les éléments d'informations personnelles, en laissant le reste du contenu tel quel

Lorsque vous anonymisez un ensemble de données, l'ensemble de données de destination ne doit pas exister avant le lancement de l'appel d'API d'anonymisation. Cette opération entraîne la création de l'ensemble de données de destination.

Lorsque vous anonymisez un seul magasin FHIR, l'ensemble de données de destination doit exister avant le lancement de l'appel d'API d'anonymisation.

L'ensemble de données source, le magasin FHIR et le magasin FHIR de l'ensemble de données de destination doivent résider dans le même projet Google Cloud. Lorsque vous exécutez l'opération d'anonymisation, l'ensemble de données de destination et son magasin FHIR sont créés dans le même projet Google Cloud que l'ensemble de données source et son magasin FHIR.

Si vous souhaitez générer des données FHIR synthétiques afin de les utiliser pour ce tutoriel, vous pouvez utiliser Synthea pour générer des données synthétiques au format FHIR STU3, copier les données générées dans un bucket Cloud Storage, puis les importer dans le magasin FHIR de l'API Cloud Healthcare. Synthea ne génère pas de données FHIR avec des composants textuels gratuits ou non structurés. Vous ne pouvez donc pas l'utiliser pour explorer ces aspects de l'anonymisation.

Pour ce tutoriel, vous allez importer des exemples de données FHIR dans le magasin FHIR, comme indiqué dans la procédure suivante.

  1. Configurez des variables d'environnement pour le projet et l'emplacement où l'ensemble de données, le magasin FHIR et les données FHIR seront stockés. Les valeurs attribuées aux variables d'environnement sont des exemples de valeurs, comme suit :

    export PROJECT_ID=MyProj
    export REGION=us-central1
    export SOURCE_DATASET_ID=dataset1
    export FHIR_STORE_ID=FHIRstore1
    export DESTINATION_DATASET_ID=deid-dataset1
    

    Les définitions des variables d'environnement déclarées dans l'exemple précédent sont les suivantes :

    • $PROJECT_ID correspond à votre identifiant de projet Google Cloud.
    • $REGION correspond à la région Google Cloud dans laquelle l'ensemble de données de l'API Cloud Healthcare est créé.
    • $SOURCE_DATASET_ID correspond au nom de l'ensemble de données de l'API Cloud Healthcare dans lequel les données sources sont stockées.
    • $FHIR_STORE_ID correspond au nom du magasin FHIR source de l'API Cloud Healthcare.
    • $DESTINATION_DATASET_ID correspond au nom de l'ensemble de données de destination de l'API Cloud Healthcare dans lequel les données anonymisées sont écrites.

    Vous utiliserez également ces variables d'environnement dans la suite de ce tutoriel.

  2. Créez un ensemble de données pour l'API Cloud Healthcare :

    gcloud healthcare datasets create $SOURCE_DATASET_ID --location=$REGION
    

    Le résultat ressemble à ce qui suit, où [OPERATION_NUMBER] est l'identifiant de l'opération de création de l'ensemble de données permettant de suivre la requête :

    Create request issued for: $SOURCE_DATASET_ID
    
    Waiting for operation [OPERATION_NUMBER] to complete...done.
    Created dataset $SOURCE_DATASET_ID.
    

    La commande ci-dessus crée l'ensemble de données source nommé $SOURCE_DATASET_ID dans la région $REGION.

  3. Créez un magasin FHIR à l'aide de la commande suivante :

    gcloud healthcare fhir-stores create $FHIR_STORE_ID \
        --dataset=$SOURCE_DATASET_ID --location=$REGION
    

    La commande ci-dessus crée un magasin FHIR nommé $FHIR_STORE_ID dans l'ensemble de données $SOURCE_DATASET_ID.

  4. Ajoutez la ressource Patient FHIR au magasin FHIR à l'aide de la fonction FHIR create avec la commande suivante :

    curl -X POST \
        -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        -H "Content-Type: application/fhir+json; charset=utf-8" \
        --data "{
           \"address\": [
        {
          \"city\": \"Anycity\",
          \"district\": \"Anydistrict\",
          \"line\": [
            \"123 Main Street\"
          ],
          \"period\": {
            \"start\": \"1990-12-05\"
          },
          \"postalCode\": \"12345\",
          \"state\": \"CA\",
          \"text\": \"123 Main Street Anycity, Anydistrict, CA 12345\",
          \"use\": \"home\"
        }
      ],
              \"name\": [
            {
              \"family\": \"Smith\",
              \"given\": [
                \"Darcy\"
              ],
              \"use\": \"official\"
            }
          ],
          \"gender\": \"female\",
          \"birthDate\": \"1980-12-05\",
          \"resourceType\": \"Patient\"
        }" \
    "https://healthcare.googleapis.com/v1/projects/$PROJECT_ID/locations/$REGION/datasets/$SOURCE_DATASET_ID/fhirStores/$FHIR_STORE_ID/fhir/Patient"
    

    L'argument de la commande correspond à l'exemple de ressource FHIR, une ressource Patient FHIR.

    {
      "address": [
        {
          "city": "Anycity",
          "district": "Anydistrict",
          "line": [
            "123 Main Street"
          ],
          "period": {
            "start": "1990-12-05"
          },
          "postalCode": "12345",
          "state": "CA",
          "text": "123 Main Street Anycity, Anydistrict, CA 12345",
          "use": "home"
        }
      ],
      "name": [
        {
          "family": "Smith",
          "given": [
            "Darcy"
          ],
    "use": "official"
        }
      ],
    "gender": "female",
    "birthDate": "1980-12-05",
     "resourceType": "Patient"
    }
    

    Si la requête aboutit, le serveur renvoie un résultat semblable à celui-ci :

    {
      "address": [
        {
          "city": "Anycity",
          "district": "Anydistrict",
          "line": [
            "123 Main Street"
          ],
          "period": {
            "start": "1990-12-05"
          },
          "postalCode": "12345",
          "state": "CA",
          "text": "123 Main Street Anycity, Anydistrict, CA 12345",
          "use": "home"
        }
      ],
      "birthDate": "1980-12-05",
      "gender": "female",
      "id": "0359c226-5d63-4845-bd55-74063535e4ef",
      "meta": {
        "lastUpdated": "2020-02-08T00:03:21.745220+00:00",
        "versionId": "MTU4MTEyMDIwMTc0NTIyMDAwMA"
      },
      "name": [
        {
          "family": "Smith",
          "given": [
            "Darcy"
          ],
          "use": "official"
        }
      ],
      "resourceType": "Patient"
    }
    

    La commande curl précédente insère une nouvelle ressource Patient dans le magasin FHIR source. Un identifiant de patient (id) est généré dans le résultat. L'identifiant de patient est une chaîne alphanumérique anonymisée utilisée dans la ressource FHIR Encounter pour associer la ressource Patient FHIR.

  5. Ajoutez la ressource Encounter FHIR au magasin FHIR à l'aide de la fonction FHIR create avec la commande suivante. Dans la commande, remplacez la valeur subject.reference par la valeur de l'identifiant du patient issue du résultat de la commande curl précédente :

    curl -X POST \
        -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        -H "Content-Type: application/fhir+json; charset=utf-8" \
        --data "{
          \"status\": \"finished\",
          \"class\": {
            \"system\": \"http://hl7.org/fhir/v3/ActCode\",
            \"code\": \"IMP\",
            \"display\": \"inpatient encounter\"
          },
          \"reason\": [
            {
              \"text\": \"Mrs. Smith is a 39-year-old female who has a past
    medical history significant for a myocardial infarction. Catheterization
    showed a possible kink in one of her blood vessels.\"
            }
          ],
          \"subject\": {
            \"reference\":
    \"Patient/0359c226-5d63-4845-bd55-74063535e4ef\"
          },
          \"resourceType\": \"Encounter\"
        }" \
    
    "https://healthcare.googleapis.com/v1/projects/$PROJECT_ID/locations/$REGION/datasets/$SOURCE_DATASET_ID/fhirStores/$FHIR_STORE_ID/fhir/Encounter"
    

    L'argument de la commande correspond à l'exemple de ressource FHIR, une ressource Encounter FHIR :

    {
          "status": "finished",
          "class": {
            "system": "http://hl7.org/fhir/v3/ActCode",
            "code": "IMP",
            "display": "inpatient encounter"
          },
          "reason": [
            {
              "text": "Mrs. Smith is a 39-year-old female who has a past medical
    history significant for a myocardial infarction. Catheterization showed a
    possible kink in one of her blood vessels."
            }
          ],
          "subject": {
            "reference": "Patient/0359c226-5d63-4845-bd55-74063535e4ef"
          },
          "resourceType": "Encounter"
        }
    

    Si la requête aboutit, le serveur renvoie un résultat semblable à celui-ci :

    {
      "class": {
        "code": "IMP",
        "display": "inpatient encounter",
        "system": "http://hl7.org/fhir/v3/ActCode"
      },
      "id": "0038a95f-3c11-4163-8c2e-10842b6b1547",
      "meta": {
        "lastUpdated": "2020-02-12T00:39:16.822443+00:00",
        "versionId": "MTU4MTQ2Nzk1NjgyMjQ0MzAwMA"
      },
      "reason": [
        {
          "text": "Mrs. Smith is a 39-year-old female who has a past medical history
    significant for a myocardial infarction. Catheterization showed a possible
    kink in one of her blood vessels."
        }
      ],
      "resourceType": "Encounter",
      "status": "finished",
      "subject": {
        "reference": "Patient/0359c226-5d63-4845-bd55-74063535e4ef"
      }
    

    La commande curl précédente insère une nouvelle ressource Encounter dans le magasin FHIR source.

Anonymiser des données FHIR

Ensuite, vous devez anonymiser les données FHIR que vous avez insérées dans le magasin FHIR source. Vous masquez ou transformez tous les éléments d'informations personnelles dans des champs structurés, tels que les champs Patient.name et Patient.address. Vous pouvez également supprimer les éléments d'informations personnelles dans les données non structurées dans du texte, tels que Encounter.reason.text.

Vous pouvez éventuellement exporter les données obtenues directement vers BigQuery à des fins d'analyse et de machine learning.

Cette configuration d'anonymisation peut être utilisée pour une analyse de l'état de santé de la population ou un cas d'utilisation similaire. Dans le contexte de ce tutoriel, vous pouvez déplacer des données structurées anonymisées vers BigQuery pour évaluer les tendances à grande échelle. Vous n'aurez peut-être pas besoin de champs non structurés, qui sont difficiles à normaliser et analyser à grande échelle. Toutefois, les champs non structurés sont inclus dans ce tutoriel comme référence.

L'anonymisation des données FHIR s'applique à de nombreux cas d'utilisation potentiels. Il existe également de nombreuses options de configuration compatibles avec l'API Cloud Healthcare. Pour obtenir plus d'informations, y compris des exemples de commandes curl et des exemples Tools for PowerShell pour différents scénarios, consultez la page Anonymiser des données FHIR.

Les champs contenant une date sont transformés par changement de date, une technique qui modifie toutes les dates d'une ressource FHIR de manière aléatoire et cohérente. Le changement de date permet de maintenir la cohérence au sein d'une ressource FHIR afin que les informations pertinentes sur le plan médical, telles que l'âge du patient et le délai entre les rendez-vous, soient conservées sans révéler d'informations permettant d'identifier le patient. Tous les identifiants de champs non structurés sont également transformés.

L'exemple suivant inclut également une transformation de hachage sur les champs name. Le hachage est une technique de chiffrement à sens unique qui garantit qu'un nom est toujours transformé vers la même valeur de sortie, ce qui génère des résultats cohérents pour le même nom de patient sur plusieurs enregistrements de l'ensemble de données. Dans cette opération, vous masquez les informations personnelles tout en conservant les liens entre les ressources.

Dans cet exemple, la clé cryptographique fournie, U2FsdGVkX19bS2oZsdbK9X5zi2utBn22uY+I2Vo0zOU=, est un exemple de clé 256 bits encodée en base64 et chiffrée par AES, générée à l'aide de la commande ci-dessous.

echo -n "test" | openssl enc -e -aes-256-ofb -a -salt

La commande vous invite à saisir un mot de passe. Saisissez le mot de passe de votre choix.

  1. Utilisez la commande curl pour masquer ou transformer tous les éléments d'informations personnelles dans les champs structurés, tels que les champs name et address, et pour transformer tous les identifiants dans les champs non structurés.

    curl -X POST \
        -H "Authorization: Bearer "$(gcloud auth print-access-token) \
        -H "Content-Type: application/json; charset=utf-8" \
        --data "{
          'destinationDataset':
    'projects/$PROJECT_ID/locations/$REGION/datasets/$DESTINATION_DATASET_ID',
          'config': {
          'fhir': {
              'fieldMetadataList': {
                'paths': [
                  'Patient.address.state',
                  'Patient.address.line',
                  'Patient.address.text',
                  'Patient.address.postalCode'
                ],
                'action': 'TRANSFORM'
              },
             'fieldMetadataList': {
               'paths': [
                 'Encounter.reason.text'
               ],
               'action': 'INSPECT_AND_TRANSFORM'
             },
           'text': {
             'transformations': [
               {
                 'infoTypes': [],
                 'replaceWithInfoTypeConfig': {}
               }
             ]
           },
              'fieldMetadataList': {
                'paths': [
                  'Patient.name.family',
                  'Patient.name.given'
                ],
                'action': 'TRANSFORM'
              },
            'text': {
              'transformations': {
                'infoTypes': [
                  'PERSON_NAME'
                ],
                'cryptoHashConfig': {
                  'cryptoKey':
    'U2FsdGVkX19bS2oZsdbK9X5zi2utBn22uY+I2Vo0zOU='
                }
              }
            },
              'fieldMetadataList': {
                'paths': [
                  'Patient.birthDate',
                  'Patient.address.period.start'
                ],
                'action': 'TRANSFORM'
              },
            'text': {
              'transformations': {
                'infoTypes': [
                  'DATE'
                ],
                'dateShiftConfig': {
                  'cryptoKey':
    'U2FsdGVkX19bS2oZsdbK9X5zi2utBn22uY+I2Vo0zOU='
                }
              }
            }
          }
        }"
    "https://healthcare.googleapis.com/v1/projects/$PROJECT_ID/locations/$REGION/datasets/$SOURCE_DATASET_ID:deidentify"
    

    Si la requête aboutit, le serveur affiche une réponse au format JSON comme suit :

    {
      "name": "projects/$PROJECT_ID/locations/$REGION/datasets/$SOURCE_DATASET_ID/OPERATION_NAME"
    }
    

    Dans l'exemple ci-dessus, la commande curl anonymise la ressource FHIR en transformant les valeurs de plusieurs manières :

    • Elle masque la valeur Patient.address.line, la valeur Patient.address.text et la valeur Patient.address.postalCode.
    • Elle remplace les valeurs Patient.name.family et Patient.name.given par des valeurs de hachage.
    • Elle remplace les valeurs des champs Patient.birthDate et period.start par les valeurs générées par le changement de date avec un différentiel de 100 jours.
    • Dans le champ Encounter.reason.text, elle remplace le nom de famille du patient par une valeur de hachage et remplace l'âge du patient par la valeur littérale [AGE].
  2. La réponse à l'opération précédente contient un nom d'opération. Utilisez la méthode get pour suivre l'état de l'opération :

    curl -X GET \
        -H "Authorization: Bearer "$(gcloud auth print-access-token) \
        -H "Content-Type: application/json; charset=utf-8" \
    "https://healthcare.googleapis.com/v1/projects/$PROJECT_ID/locations/$REGION/datasets/$SOURCE_DATASET_ID/operations/OPERATION_NAME"
    

    Si la requête aboutit, le serveur affiche une réponse au format JSON. Une fois le processus d'anonymisation terminé, la réponse inclut "done": true.

    {
      "name": "projects/$PROJECT_ID/locations/$REGION/datasets/$SOURCE_DATASET_ID/operations/OPERATION_NAME",
      "metadata": {
        "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
        "apiMethodName": "google.cloud.healthcare.v1.dataset.DatasetService.DeidentifyDataset",
        "createTime": "2018-01-01T00:00:00Z",
        "endTime": "2018-01-01T00:00:00Z"
      },
      "done": true,
      "response": {
        "@type": "...",
        "successStoreCount": "SUCCESS_STORE_COUNT"
      }
    }
    

    La commande précédente renvoie l'état de l'opération d'anonymisation.

  3. Utilisez l'identifiant du patient pour obtenir les détails de la ressource Patient FHIR dans le nouvel ensemble de données de destination en exécutant la commande suivante :

    curl -X GET \
         -H "Authorization: Bearer $(gcloud auth print-access-token)" \
     "https://healthcare.googleapis.com/v1/projects/$PROJECT_ID/locations/$REGION/datasets/$DESTINATION_DATASET_ID/fhirStores/$FHIR_STORE_ID/fhir/Patient/a952e409-2403-43e6-9815-cb78c5b5eca2/\$everything"
    

    Si la requête aboutit, le serveur renvoie une réponse semblable à la suivante, qui est la version anonymisée des ressources FHIR d'origine :

      "entry": [\
        {\
          "resource": {\
            "class": {\
              "code": "IMP",\
              "display": "inpatient encounter",\
              "system": "http://hl7.org/fhir/v3/ActCode"\
            },\
            "id": "0038a95f-3c11-4163-8c2e-10842b6b1547",\
            "reason": [\
              {\
                "text": "Mr. NlVBV12Hhb5DD8WNqlTpXboFxzlUSlqAmYDet/jIViQ= is a [AGE]
    gentleman who has a past medical history significant for a myocardial
    infarction. Catheterization showed a possible kink in one of his vessels."\
              }\
            ],\
            "resourceType": "Encounter",\
            "status": "finished",\
            "subject": {\
              "reference": "Patient/0359c226-5d63-4845-bd55-74063535e4ef"\
            }\
          }\
        },\
        {\
          "resource": {\
            "address": [\
              {\
                "city": "Anycity",\
                "district": "Anydistrict",\
                "line": [\
                  ""\
                ],\
                "period": {\
                  "start": "1990-09-23"\
                },\
                "postalCode": "",\
                "state": "",\
                "text": "",\
                "use": "home"\
              }\
            ],\
            "birthDate": "1980-09-23",\
            "gender": "female",\
            "id": "0359c226-5d63-4845-bd55-74063535e4ef",\
            "name": [\
              {\
                "family": "NlVBV12Hhb5DD8WNqlTpXboFxzlUSlqAmYDet/jIViQ=",\
                "given": [\
                  "FSH4e the project.D/IGb80a1rS0L0kqfC3DCDt6//17VPhIkOzH2pk="\
                ],\
                "use": "official"\
              }\
            ],\
            "resourceType": "Patient"\
          }\
        }\
      ],\
      "resourceType": "Bundle",\
      "total": 2,\
      "type": "searchset"\
    }
    

    La commande précédente vérifie que l'opération d'anonymisation a réussi à anonymiser les ressources FHIR.

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

Supprimer le projet

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Supprimer les ressources individuelles

  • Supprimez les ensembles de données de destination :

    gcloud healthcare datasets delete $DESTINATION_DATASET_ID
    

Étape suivante