Usar la API Cloud Healthcare para desidentificar datos clínicos de FHIR


En este tutorial se explica a investigadores, científicos de datos y equipos de TI que trabajan con organizaciones sanitarias y del ámbito de las ciencias biológicas cómo usar la operación de desidentificación Fast Healthcare Interoperability Resources (FHIR) de la API Cloud Healthcare para eliminar o modificar información personal identificable (IPI), incluida la información médica protegida (IMP), de los datos clínicos FHIR. La desidentificación de los datos médicos ayuda a proteger la privacidad de los pacientes y a preparar los datos sanitarios para su uso en investigaciones, intercambio de datos y aprendizaje automático.

En este tutorial se da por hecho que tienes conocimientos básicos de Linux. También es útil tener un conocimiento básico de Google Cloud y de la especificación FHIR y de su uso en sistemas de historias clínicas electrónicas (HCE). Ejecuta todos los comandos de este tutorial en Cloud Shell.

Objetivos

  • Crea un conjunto de datos y un almacén FHIR de la API Cloud Healthcare.
  • Importa datos FHIR al almacén FHIR de la API de Cloud Healthcare.
  • Usa la operación de desidentificación de FHIR de la API de Cloud Healthcare para eliminar o modificar la información personal identificable y la información médica protegida de las instancias FHIR de un almacén FHIR.
  • Usa la herramienta de línea de comandos curl para hacer una llamada de desidentificación de FHIR a través de la API Cloud Healthcare.

Costes

En este tutorial se usan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Antes de empezar

La API Cloud Healthcare se usa siempre en el contexto de unGoogle Cloud proyecto. Los proyectos son la base para crear, habilitar y usar todos losGoogle Cloud servicios Google Cloud, lo que incluye gestionar APIs, habilitar la facturación, añadir y quitar colaboradores, y gestionar los permisos de los recursos. Sigue este procedimiento para crear un proyecto o selecciona uno que ya hayas creado. Google Cloud

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

    Go to project selector

  2. Select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.
  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the Cloud Healthcare API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    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. En Cloud Shell, ejecuta el comando gcloud components update para asegurarte de que tienes la versión más reciente de la CLI de gcloud, que incluye funciones relacionadas con la API Cloud Healthcare.

Cuando termines las tareas que se describen en este documento, puedes evitar que se te siga facturando eliminando los recursos que has creado. Para obtener más información, consulta la sección Limpiar.

Crear una cuenta de servicio de IAM

Para completar este tutorial, necesitas los roles Administrador de conjuntos de datos de Healthcare, Administrador de FHIR y Editor de recursos FHIR. Sigue estos pasos para crear una cuenta de servicio y asignarle los roles correctos:

  1. Crear cuentas de servicio
  2. Asigna roles a la cuenta de servicio:

    • Administrador de conjuntos de datos del sector sanitario
    • Administrador de recursos FHIR del sector sanitario
    • Editor de recursos FHIR del sector sanitario
  3. Crea y descarga la clave JSON de la cuenta de servicio.

  4. Activa la clave de tu cuenta de servicio:

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

    El resultado es el siguiente:

    Activated service account credentials for: [key-name@project-name.iam.gserviceaccount.com]
    
    • key-name es el nombre que has asignado a la clave de cuenta de servicio.
    • project-name es el nombre de tu Google Cloud proyecto.

Obtener un token de acceso OAuth 2.0

Para usar la API Cloud Healthcare con el fin de ingerir datos, necesitas un token de acceso OAuth 2.0 que obtendrás con los comandos de este tutorial. En este tutorial, algunas de las solicitudes de ejemplo de la API Cloud Healthcare usan la herramienta de línea de comandos curl. En estos ejemplos se usa el comando gcloud auth print-access-token para obtener un token de portador de OAuth 2.0 y para incluirlo en el encabezado de autorización de la solicitud. Para obtener más información sobre este comando, consulta gcloud auth application-default print-access-token.

Configurar el conjunto de datos FHIR para la desidentificación

Cada recurso FHIR es un objeto similar a JSON que contiene pares clave-valor. Algunos elementos están estandarizados, mientras que otros son de texto libre. Puedes usar la operación de desidentificación para hacer lo siguiente:

  • Elimina los valores de claves específicas del recurso FHIR.
  • Procesar el texto no estructurado para eliminar solo los elementos de IPI y dejar tal cual el resto del contenido del texto.

Cuando anonimizas un conjunto de datos, el conjunto de datos de destino no debe existir antes de hacer la llamada a la API de anonimización. La operación de desidentificación crea el conjunto de datos de destino.

Cuando desidentificas un solo almacén FHIR, el conjunto de datos de destino debe existir antes de hacer la llamada a la API de desidentificación.

El conjunto de datos de origen, el almacén FHIR y el almacén FHIR del conjunto de datos de destino deben estar en el mismo Google Cloud proyecto. Cuando ejecutas la operación de desidentificación, el conjunto de datos de destino y el almacén FHIR se crean en el mismo Google Cloud proyecto que el conjunto de datos de origen y el almacén FHIR.

Si quieres generar datos FHIR sintéticos para este tutorial, puedes usar Synthea para generar datos sintéticos en formato FHIR STU3, copiar los datos generados en un segmento de Cloud Storage y, a continuación, importarlos al almacén FHIR de la API Cloud Healthcare. Synthea no genera datos FHIR con componentes de texto libre o no estructurado, por lo que no puedes usarlo para explorar estos aspectos de la desidentificación.

En este tutorial, importará datos FHIR de ejemplo en el almacén FHIR, tal como se indica en el siguiente procedimiento.

  1. Configura las variables de entorno del proyecto y la ubicación en los que se almacenarán el conjunto de datos, el almacén FHIR y los datos FHIR. Los valores asignados a las variables de entorno son valores de ejemplo, como se indica a continuación:

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

    Las definiciones de las variables de entorno que se declaran en el ejemplo anterior son las siguientes:

    • $PROJECT_ID es el identificador de tu Google Cloud proyecto.
    • $REGION es la región en la que se crea el conjunto de datos de la API Cloud Healthcare. Google Cloud
    • $SOURCE_DATASET_ID es el nombre del conjunto de datos de la API Cloud Healthcare donde se almacenan los datos de origen.
    • $FHIR_STORE_ID es el nombre del almacén FHIR de la API Cloud Healthcare de origen.
    • $DESTINATION_DATASET_ID es el nombre del conjunto de datos de destino de la API Cloud Healthcare en el que se escriben los datos desidentificados.

    También usarás estas variables de entorno más adelante en este tutorial.

  2. Crea un conjunto de datos de la API Cloud Healthcare:

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

    El resultado es similar al siguiente, donde [OPERATION_NUMBER] es el identificador de la operación de creación del conjunto de datos que se usa para hacer un seguimiento de la solicitud:

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

    El comando anterior crea el conjunto de datos de origen con el nombre $SOURCE_DATASET_ID en la región $REGION.

  3. Crea un almacén FHIR con el siguiente comando:

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

    El comando anterior crea un almacén FHIR con el nombre $FHIR_STORE_ID en el conjunto de datos $SOURCE_DATASET_ID.

  4. Añade el recurso Patient de FHIR al almacén FHIR mediante la función create de FHIR con el siguiente comando:

    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"
    

    El argumento del comando se corresponde con el recurso FHIR de ejemplo, un recurso Patient de 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 solicitud se realiza correctamente, el servidor devuelve un resultado como el siguiente:

    {
      "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"
    }
    

    El comando curl anterior inserta un nuevo recurso Patient en el almacén FHIR de origen. En la salida se genera un identificador de paciente (id). El identificador del paciente es una cadena alfanumérica anonimizada que se usa en el recurso Encounter de FHIR para vincularlo al recurso Patient de FHIR.

  5. Añade el recurso FHIR Encounter al almacén FHIR mediante la función FHIR create con el siguiente comando. En el comando, sustituya el valor subject.reference por el valor del identificador del paciente de la salida del comando curl anterior:

    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"
    

    El argumento del comando se corresponde con el recurso FHIR de ejemplo, un recurso FHIR Encounter:

    {
          "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 solicitud se realiza correctamente, el servidor devuelve un resultado como el siguiente:

    {
      "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"
      }
    

    El comando curl anterior inserta un nuevo recurso Encounter en el almacén FHIR de origen.

Desidentificar datos FHIR

A continuación, desidentifica los datos FHIR que has insertado en el almacén FHIR de origen. Puede ocultar o transformar todos los elementos de IPI en campos estructurados, como los campos Patient.name y Patient.address. También debes anonimizar los elementos de IPI en los datos no estructurados en texto, como Encounter.reason.text.

También puede exportar los datos resultantes directamente a BigQuery para analizarlos y entrenar modelos de aprendizaje automático.

Esta configuración de desidentificación se puede usar para un análisis de salud de la población o un caso práctico similar. En el contexto de este tutorial, puedes mover datos estructurados anonimizados a BigQuery para evaluar tendencias a gran escala. Es posible que no necesites campos no estructurados, que son difíciles de normalizar y analizar a gran escala. Sin embargo, los campos no estructurados se incluyen en este tutorial como referencia.

Hay muchos casos prácticos en los que se pueden desidentificar datos FHIR. La API Cloud Healthcare también admite muchas opciones de configuración. Para obtener más información, incluidos ejemplos de comandos de curl y de herramientas para PowerShell en diferentes situaciones, consulta Desidentificar datos FHIR.

Los campos que contienen una fecha se transforman mediante el desplazamiento de fechas, una técnica que cambia todas las fechas de un recurso FHIR en una cantidad aleatoria y constante. El cambio de fecha mantiene la coherencia en un recurso FHIR para que se conserven los detalles relevantes desde el punto de vista médico, como la edad del paciente y el tiempo transcurrido entre las citas, sin revelar información identificativa sobre el paciente. También se transforman todos los identificadores de los campos no estructurados.

En el ejemplo siguiente también se incluye una transformación de cifrado hash en los campos name. El cifrado con hash es una técnica de cifrado unidireccional que asegura que un nombre se transforme siempre en el mismo valor de salida, lo que genera resultados coherentes para el mismo nombre de paciente en varios registros del conjunto de datos. En esta operación, se oculta la información personal identificable y, al mismo tiempo, se conservan los enlaces entre los recursos.

En este ejemplo, la clave criptográfica proporcionada, U2FsdGVkX19bS2oZsdbK9X5zi2utBn22uY+I2Vo0zOU=, es una clave de 256 bits cifrada con AES y codificada en Base64 que se genera con el siguiente comando.

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

El comando te pedirá que introduzcas una contraseña. Introduce la contraseña que quieras.

  1. Usa el comando curl para ocultar o transformar todos los elementos de IPI de los campos estructurados, como los campos name y address, y para transformar todos los identificadores de los campos no estructurados.

    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 solicitud se realiza de forma correcta, el servidor devuelve una respuesta en formato JSON como la siguiente:

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

    En el ejemplo anterior, el comando curl desidentifica el recurso de FHIR transformando los valores de las siguientes formas:

    • Redacta el valor Patient.address.line, el valor Patient.address.text y el valor Patient.address.postalCode.
    • Sustituye el valor Patient.name.family por un valor hash y el valor Patient.name.given por otro valor hash.
    • Sustituye los valores de los campos Patient.birthDate y period.start por valores que se obtienen al cambiar la fecha con una diferencia de 100 días.
    • En el campo Encounter.reason.text, sustituye el apellido del paciente por un valor hash y sustituye la edad del paciente por el valor literal [AGE].
  2. La respuesta a la operación anterior contiene un nombre de operación. Utiliza el método get para hacer un seguimiento del estado de la operación:

    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 solicitud se realiza de forma correcta, el servidor devuelve una respuesta en formato JSON. Una vez completado el proceso de desidentificación, la respuesta incluye "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"
      }
    }
    

    El comando anterior devuelve el estado de la operación de desidentificación.

  3. Usa el identificador del paciente para obtener los detalles del recurso Patient de FHIR en el nuevo conjunto de datos de destino ejecutando el siguiente comando:

    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 solicitud se realiza correctamente, el servidor devuelve una respuesta como la siguiente, que es la versión anonimizada de los recursos FHIR originales:

      "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"\
    }
    

    El comando anterior verifica que la operación de desidentificación se haya realizado correctamente en los recursos FHIR.

Limpieza

Para evitar que los recursos utilizados en este tutorial se cobren en tu cuenta de Google Cloud, elimina el proyecto que contiene los recursos o conserva el proyecto y elimina los recursos.

Eliminar el proyecto

  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.

Eliminar los recursos concretos

  • Elimina los conjuntos de datos de destino:

    gcloud healthcare datasets delete $DESTINATION_DATASET_ID
    

Siguientes pasos