Como usar a API Cloud Healthcare para desidentificar dados médicos de FHIR


Este tutorial mostra a pesquisadores, cientistas de dados e equipes de TI que trabalham com organizações de saúde e ciências biológicas como usar a operação de desidentificação de Recursos rápidos de interoperabilidade de saúde (FHIR, na sigla em inglês) da API Cloud Healthcare para remover ou modificar informações de identificação pessoal (PII, na sigla em inglês), incluindo Informações protegidas de saúde (PHI, na sigla em inglês), a partir de dados clínicos de FHIR. A desidentificação de dados médicos ajudam a proteger a privacidade do paciente e a preparar dados de saúde para uso em pesquisa, compartilhamento de dados e machine learning.

Neste tutorial, pressupõe-se que você tenha um conhecimento fundamental de Linux. Também é desejável ter conhecimentos básicos do Google Cloud, da Especificação FHIR e de seu uso nos sistemas de registros eletrônicos de saúde (EHRs, na sigla em inglês). Execute todos os comandos neste tutorial no Cloud Shell.

Objetivos

  • Crie um conjunto de dados e um armazenamento FHIR da API Cloud Healthcare.
  • Importe dados FHIR para o armazenamento FHIR da API Cloud Healthcare.
  • Use a operação de desidentificação de FHIR na API Cloud Healthcare para remover ou modificar PII e PHI em instâncias de FHIR em um armazenamento de FHIR.
  • Use a ferramenta de linha de comando curl para fazer uma chamada de desidentificação FHIR por meio da API Cloud Healthcare.

Custos

Neste tutorial, usamos o seguinte componente faturável do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços.

Antes de começar

Todo o uso da API Cloud Healthcare ocorre no contexto de um projeto do Google Cloud. Os projetos formam a base para criar, ativar e usar todos os serviços do Google Cloud, incluindo o gerenciamento de APIs, ativação de faturamento, adição e remoção de colaboradores e gerenciamento de permissões dos recursos do Google Cloud. Use o procedimento a seguir para criar um projeto do Google Cloud ou selecionar um projeto que você já tenha criado.

  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. No Cloud Shell, execute o comando gcloud components update para garantir que você tenha a versão mais recente da CLI gcloud que inclui a funcionalidade relacionada à API Cloud Healthcare.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Como criar uma conta de serviço do IAM

Os papéis de Administrador de conjunto de dados do Healthcare, Administrador de FHIR e Editor de recursos de FHIR são necessários para este tutorial. Use as etapas a seguir para criar uma conta de serviço e atribuir os papéis corretos:

  1. Crie uma conta de serviço.
  2. Atribua papéis à conta de serviço:

    • Administrador do conjunto de dados da API Cloud Healthcare
    • Administrador de FHIR da API Cloud Healthcare
    • Editor de recursos do FHIR do Healthcare
  3. Crie e faça o download da chave JSON da conta de serviço.

  4. Ative a chave da sua conta de serviço:

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

    A saída é esta:

    Activated service account credentials for: [key-name@project-name.iam.gserviceaccount.com]
    
    • key-name é o nome atribuído à chave da conta de serviço.
    • project-name é o nome do projeto do Google Cloud.

Como conseguir um token de acesso do OAuth 2.0

Para usar a API Cloud Healthcare para processar dados, você precisa de um token de acesso do OAuth 2.0 que os comandos deste tutorial conseguem para você. Neste tutorial, algumas das solicitações de exemplo da API Cloud Healthcare usam a ferramenta de linha de comando curl. Esses exemplos usam o comando gcloud auth print-access-token para receber um token do portador OAuth 2.0 e incluir o token no cabeçalho de autorização da solicitação. Para mais informações sobre esse comando, consulte gcloud auth application-default print-access-token.

Como configurar o conjunto de dados FHIR para desidentificação

Cada recurso FHIR é um objeto semelhante a JSON que contém pares de chave-valor. Alguns elementos são padronizados e outros são texto livre. É possível usar a operação de desidentificação para fazer o seguinte:

  • Remova os valores de chaves específicas no recurso FHIR.
  • Processe o texto não estruturado para remover apenas os elementos de PII, deixando o restante do conteúdo no texto como está.

Quando você desidentifica um conjunto de dados, o conjunto de dados de destino não pode existir antes de você fazer a chamada da API de desidentificação. A operação de desidentificação cria o conjunto de dados de destino.

Quando você desidentifica um único armazenamento de FHIR, o conjunto de dados de destino precisa existir antes de fazer a chamada da API de desidentificação.

O conjunto de dados de origem, o armazenamento de FHIR e o armazenamento de FHIR do conjunto de dados de destino precisam residir no mesmo projeto do Google Cloud. Quando você executa a operação de desidentificação, o conjunto de dados de destino e o armazenamento de FHIR são criados no mesmo projeto do Google Cloud que o conjunto de dados de origem e o armazenamento de FHIR.

Se quiser gerar dados de FHIR sintéticos para serem usados neste tutorial, use o Synthea para gerar dados sintéticos no formato FHIR STU3, copie os dados gerados para um bucket do Cloud Storage e importe-o para o armazenamento FHIR da API Cloud Healthcare. O Synthea não gera dados FHIR com componentes de texto livres ou não estruturados. Portanto, não é possível usá-los para explorar esses aspectos da desidentificação.

Para este tutorial, importe os dados de FHIR de amostra para o armazenamento de FHIR, conforme indicado no procedimento a seguir.

  1. Configure variáveis de ambiente para o projeto e local onde o conjunto de dados, o armazenamento de FHIR e os dados de FHIR serão armazenados. Os valores atribuídos às variáveis de ambiente são valores de amostra, da seguinte maneira:

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

    As definições das variáveis de ambiente declaradas no exemplo anterior são as seguintes:

    • $PROJECT_ID é o identificador do projeto do Google Cloud.
    • $REGION é a região do Google Cloud em que o conjunto de APIs Cloud Healthcare é criado.
    • $SOURCE_DATASET_ID é o nome do conjunto de dados da API Cloud Healthcare em que os dados de origem são armazenados.
    • $FHIR_STORE_ID é o nome do armazenamento de FHIR de origem da API Cloud Healthcare.
    • $DESTINATION_DATASET_ID é o nome do conjunto de dados de destino da API Cloud Healthcare em que os dados desidentificados são gravados.

    Você também usará essas variáveis de ambiente posteriormente neste tutorial.

  2. Crie um conjunto de dados da API Cloud Healthcare:

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

    A saída é semelhante à seguinte, em que [OPERATION_NUMBER] é o identificador da operação de criação do conjunto de dados usado para rastrear a solicitação:

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

    O comando anterior cria o conjunto de dados de origem com o nome $SOURCE_DATASET_ID na região $REGION.

  3. Crie um armazenamento FHIR usando o seguinte comando:

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

    O comando anterior cria um armazenamento de FHIR com o nome $FHIR_STORE_ID no conjunto de dados $SOURCE_DATASET_ID.

  4. Adicione o recurso FHIR do paciente ao armazenamento de FHIR usando a função create de FHIR com o seguinte 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"
    

    O argumento do comando corresponde ao recurso de FHIR de exemplo, um recurso de paciente 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"
    }
    

    Se a solicitação for bem-sucedida, o servidor retornará uma saída como a seguinte:

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

    O comando curl anterior insere um novo recurso de paciente no armazenamento FHIR de origem. Um identificador de paciente (id) é gerado na saída. O identificador do paciente é uma string alfanumérica desidentificada que é usada no recurso FHIR Encounter para vinculação com o recurso FHIR Patient.

  5. Adicione o recurso FHIR Encounter ao armazenamento de FHIR usando a função create de FHIR com o comando a seguir. No comando, substitua o valor subject.reference pelo valor do identificador do paciente da saída do 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"
    

    O argumento do comando corresponde ao recurso de FHIR de exemplo, um 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"
        }
    

    Se a solicitação for bem-sucedida, o servidor retornará uma saída como a seguinte:

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

    O comando curl anterior insere um novo recurso Encounter no armazenamento FHIR de origem.

Como desidentificar os dados de FHIR

Em seguida, desidentifique os dados FHIR inseridos no armazenamento FHIR de origem. Você edita ou transforma todos os elementos de PII em campos estruturados, como os campos Patient.name e Patient.address. Você também desidentifica os elementos de PII nos dados não estruturados em texto, como Encounter.reason.text.

Se preferir, exporte os dados resultantes diretamente para o BigQuery para análise e treinamento de machine learning.

Essa configuração de desidentificação pode ser usada para uma análise de saúde da população ou um caso de uso semelhante. No contexto deste tutorial, você pode mover dados estruturados desidentificados para o BigQuery para avaliar tendências em grande escala. Talvez você não precise de campos não estruturados, que são difíceis de normalizar e analisar em grande escala. No entanto, os campos não estruturados são incluídos neste tutorial como referência.

Há muitos casos de uso em potencial para desidentificar dados FHIR. Há também muitas opções de configuração compatíveis com a API Cloud Healthcare. Para mais informações, incluindo comandos curl de amostra e exemplos de ferramentas para PowerShell para diferentes cenários, consulte Como desidentificar dados FHIR.

Os campos que contêm uma data são transformados por mudança de data, uma técnica que altera todas as datas em um recurso FHIR por uma quantidade consistente e aleatória. A mudança de data mantém a consistência em um recurso FHIR para que detalhes clinicamente relevantes, como a idade e o tempo entre os consultas, sejam mantidos sem revelar informações de identificação do paciente. Todos os identificadores em campos não estruturados também são transformados.

O exemplo a seguir também inclui uma transformação de hash nos campos name. O hash é uma técnica de criptografia unidirecional que garante que um nome seja sempre transformado no mesmo valor de saída, gerando saídas consistentes para o mesmo nome de paciente em vários registros no conjunto de dados. Nessa operação, você oculta as PII e também mantém links entre os recursos.

Neste exemplo, a chave criptográfica fornecida, U2FsdGVkX19bS2oZsdbK9X5zi2utBn22uY+I2Vo0zOU=, é uma chave criptografada com base em AES de 256 bits gerada usando o comando a seguir.

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

O comando solicita que você digite uma senha. Digite uma senha de sua escolha.

  1. Use o comando curl para editar ou transformar todos os elementos de PII em campos estruturados, como os campos name e address, e para transformar todos os identificadores em campos não estruturados.

    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"
    

    Se a solicitação for bem-sucedida, o servidor retornará uma resposta no formato JSON como a seguir:

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

    No exemplo anterior, o comando curl desidentifica o recurso FHIR transformando valores das seguintes maneiras:

    • Edita os valores Patient.address.line, Patient.address.text e Patient.address.postalCode.
    • Substitui o valor Patient.name.family por um valor de hash e o valor Patient.name.given por um valor de hash.
    • Substitui os valores nos campos Patient.birthDate e period.start por valores produzidos pela alteração da data com um diferencial de 100 dias.
    • No campo Encounter.reason.text, substitui o nome da família do paciente por um valor de hash e substitui a idade do paciente pelo valor literal [AGE].
  2. A resposta à operação anterior contém um nome de operação. Use o método get para acompanhar o status da operação:

    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"
    

    Se a solicitação for bem-sucedida, o servidor retornará uma resposta no formato JSON: Depois que o processo de desidentificação for concluído, a resposta incluirá "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"
      }
    }
    

    O comando anterior retorna o status da operação de desidentificação.

  3. Use o identificador do paciente para ver os detalhes do recurso FHIR Patient no novo conjunto de dados de destino executando o seguinte 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"
    

    Se a solicitação for bem-sucedida, o servidor retornará uma resposta como a seguinte, que é a versão desidentificada dos recursos FHIR originais:

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

    O comando anterior verifica se a operação de desidentificação foi bem-sucedida durante a desidentificação dos recursos do FHIR.

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

Exclua o projeto

  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.

Excluir recursos individuais

  • Exclua os conjuntos de dados de destino:

    gcloud healthcare datasets delete $DESTINATION_DATASET_ID
    

A seguir