Reticulate: execute scripts do Python sem sair do R

Aprenda como executar scripts do Python sem sair do R com o pacote reticulate!

O reticulate é um pacote do R que realiza uma integração entre sessões do R e Python. Eu recentemente descobri esse pacote e estou muito feliz em executar scripts do Python sem sair do R, é uma verdadeira união do útil ao agradável.

Integrando R e Python com o reticulate

Nesse post eu vou demonstrar o reticulate com dois scripts. Primeiro, eu vou começar uma sessão no R com um script R. Em seguida, vou executar um script Python e manipular o output dele dentro da sessão do R.

O resultado da demonstração será uma tabela (data frame) contendo as coordenadas dos exons de dois genes e as sequências de nucleotídeos desses exons.

O código dessa demonstração está salvo no meu portfólio no GitHub.

Script do R (parte 1/2)

Instalando pacotes

Vou começar listando os pacotes do R que usarei na demonstração. Lembre-se de que pode instalar os pacotes no R com o comando install.packages() ou BiocManager::install() caso sejam da família Bioconductor (por sua vez o BiocManager deve ser instalado com o install.packages() também).

library(here)
library(tidyverse)
library(reticulate)
library(glue)

# Bioconductor
library(GenomicRanges)
library(BSgenome)
library(BSgenome.Hsapiens.UCSC.hg38)

Contéudo do script

Em seguida, usando o pacote here, crio o caminho completo de um arquivo GFF3 e atribuo ao objeto gffPath. O formato GFF3 serve para armazenar propriedades de genes num arquivo de texto para ajudar a representar dados genômicos. O arquivo em questão contém propriedades de todo o genoma humano. No entanto, como é um arquivo grande, não coloquei no meu repositório GitHub, mas você pode baixá-lo no site FTP do NCBI.

gffPath <- here("GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.gff")

Edite o caminho do arquivo usando a sintaxe do here caso necessário.

Em seguida, crio um vetor simples no R com o nome de apenas dois genes para deixar a demonstração simples:

genes <- c("HTT", "FMR1")

Apresentando as funções do reticulate

Agora posso executar o script do Python. Caso você utilize ambientes virtuais conda como eu, você pode selecionar o ambiente desejado com a função use_condaenv() do reticulate. Dessa forma, usarei meu ambiente chamado de bioenv:

use_condaenv("bioenv")

Existe uma função análoga para o uso de ambientes virtualenv do Python, a use_virtualenv().

Em seguida, usando o here novamente, configuro o caminho para o script Python e depois o executo com a função source_python() do reticulate:

pythonScript <- here("reticulate_demo_Python_side.py")

Agora vou mostrar o conteúdo desse script.

Script Python

Os módulos Python que usarei nessa demonstração são o pyranges e o pandas. Acredito que o pyranges executa o pandas em segundo plano, mas como diria o Zen do Python, “explícito é melhor que implícito”.

import pyranges as pr
import pandas as pd

Instalando módulos

(Lembre-se de instalar os módulos por meio do conda ou pip):

conda install -c bioconda pyranges
# ou
pip install pyranges

Lendo o arquivo GFF3

Em seguida, carrego o arquivo GFF3 na sessão Python usando a função read_gff3() do pyranges. Já que eu atribuí o caminho do arquivo no objeto gffPath do R, devo usar o prefixo r. para trazê-lo para a sessão do Python, dessa forma:

grch38_gff = pr.read_gff3(r.gffPath)

Convertendo em data frame

Observe que atribuí o conteúdo do arquivo ao objeto grch38_gff. Nesse ponto, posso transformar o objeto num data frame do pandas para facilitar a busca pelos genes que defini anteriormente. Faço isso apenas adicionado o sufixo .df ao nome do objeto:

df = grch38_gff.df

Observe que isso é um método herdado do pyranges, nem todo objeto pode ser convertido num data frame da mesma forma. Consulte os métodos do módulo em questão para saber como fazer tal operação.

Buscando os genes

A vantagem de transformar o objeto pyranges em pandas é que eu posso encontrar os genes desejados por meio do método str.contains(). Portanto, é necessário que eu crie uma string de expressão regular (regex). Farei isso sufixando o caractere $ ao fim de cada gene. Esse caractere, indica, no regex, que a palavra buscada deve estar no fim de uma frase, o que no nosso caso, vai garantir que encontremos o nome completo do gene:

suffix = "$"
search_string = "|".join([gene + suffix for gene in r.genes])

Observe que usei uma list comprehension do Python e o método .join() para criar a string regex e atribuo a ela o objeto search_string. O caractere | (ou pipe) significa “ou” no regex. O resultado é este:

print(search_string)
# HTT$|FMR1$

Filtrando o data frame

Finalmente posso filtrar o data frame para capturar as coordenadas dos genes desejados usando a sintaxe de filtragem do pandas:

df = df[(df["gene"].str.contains(search_string)) & (df["Feature"] == "exon")]

Observe que estou sobrescrevendo o objeto df com o resultado da própria filtragem para facilitar a transferência do objeto para a sessão do R. Veja também que estou trabalhando com duas colunas distintas: a coluna gene (df["gene"]) e Feature (df["Feature"]). A primeira coluna estou filtrando com a string regex criada acima enquanto a segunda coluna estou filtrando para que contenha apenas a característica (feature) exons. Em outras palavras, esse comando selecionará as coordenadas dos exons dos genes desejados.

O comando acima conclui o script Python. Vamos retornar ao script R para terminar a demonstração do reticulate.

Script R: parte 2/2

Nós podemos trazer objetos Python para a sessão do R de maneira similar à apontada para o caminho inverso. No R usamos o prefixo py$. Dessa forma, vou trazer o objeto df da sessão Python e atribuí-lo ao objeto exonsCoordinates do R:

exonsCoordinates <- py$df

Função customizada getSequence()

Antes de continuar, permita-me trazer uma função que criei que irá recuperar as sequências exônicas usando os pacotes GenomicRanges (o pyranges é o análogo Pythonico deste), BSgenome e BSgenome.Hsapiens.UCSC.hg38:

source(here("src", "getSequence.R"))

# Código da função (arquivo getSequence.R):

getSequence <- function(chr, start, end) {
gr <- GenomicRanges::GRanges(glue::glue("{chr}:{start}-{end}")) 
refBase <- BSgenome::getSeq(BSgenome.Hsapiens.UCSC.hg38::BSgenome.Hsapiens.UCSC.hg38, gr)
refBase <- as.data.frame(refBase)$x[1]

return(refBase)
}

Importante: o primeiro uso do BSgenome/BSgenome.Hsapiens.UCSC.hg38 realizará o download de arquivos de sequência do genoma humano que serão salvos numa localização específica do seu computador. Certifique-se que possui cota de transmissão de dados e espaço suficiente.

Como o nome do terceiro pacote indica, estou utilizando a versão (build) GRCh38 do genoma humano.

Finalizando

Finalmente, usando a sintaxe de pipes (%>%) da família de pacotes tidyverse (mais especificamente o dplyr), crio uma nova coluna no data frame (com a função mutate()) usando as colunas Chromosome, Start e End como argumentos para a função getSequence() mencionada acima. Essa nova coluna conterá as sequências de nucleotídeos dos exons. Observe o uso das funções rowwise() e ungroup(). Já que minha função não é vetorizada, devo usá-la linha-a-linha em vez de coluna-a-coluna como é o comportamento padrão do R. A primeira função garante isso, enquanto a segunda restaura o data frame ao comportamento padrão assim que a getSequence() termina seu trabalho. Em seguida, finalizo selecionando apenas algumas colunas do data frame com a função select():

exonsCoordinatesClean <- exonsCoordinates %>%  
rowwise() %>% 
mutate(sequence = getSequence(Chromosome, Start, End)) %>% 
ungroup() %>% 
select(Chromosome, Feature, Start, End, Score, Strand, Frame, ID, gene, sequence)

O resultado é este:

Reticulate para integrar R e Python

Conclusão

  • Demonstrei como executar um script Python com o reticulate sem sair de uma sessão ativa do R;
  • Expliquei como recuperar objetos Python em uma sessão R e vice-versa;
  • Demonstrei uma função simples que recupera sequências de nucleotídeos a partir do genoma de referência humano (build GRCh38) usando coordenadas cromossômicas.
Relacionados