Considere o seguinte df
:
ID d1 d2
1 G G
2 A G
3 A A
4 G A
5 NA NA
6 G G
Ao unir d1
e d2
:
tidyr::unite(df, new, d1, d2, remove = FALSE, sep = "")
A linha 5 dá NANA
vez do esperado NA
ID new d1 d2
1 1 GG G G
2 2 AG A G
3 3 AA A A
4 4 GA G A
5 5 NANA <NA> <NA>
6 6 GG G G
unite()
está apenas seguindo as regras de colagem padrão:
paste(NA, NA)
#> [1] "NA NA"
Eu estava pensando em um tratamento de pré-processamento semelhante a: with(df, ifelse(is.na(d1)|is.na(d2), NA, paste0(d1, d2)))
.
Acho que você precisa de um argumento convincente sobre por que unite()
deveria funcionar de maneira diferente de paste()
Bem, eu acho que unite()
_deve_ funcionar como paste()
mas talvez pudesse fornecer um argumento adicional para lidar com NA
s, à la na.rm = TRUE
Acho que, em alguns casos, a opção omitir NA pode ser útil. Meu df
tem muitas colunas que contêm principalmente NA, como resultado de várias rodadas de junção.
recipe potato tomato cucumber rock
A potato NA cucumber NA
B NA NA NA rock
C NA tomato NA NA
...
Então, eu estava tentando combinar as colunas em uma e remover o NA para ver as coisas melhor.
recipe ingredients
A potato,cucumber
B rock
C tomato
...
A solução não é difícil, apenas não tão organizada.
Acabei de me deparar com esse problema e também sugerir a adição de uma opção para lidar com NA em união. Na verdade, eu sugeriria que as seguintes expressões (embora talvez com um parâmetro extra para omitir NAs na unidade) devem produzir uma saída idêntica à sua entrada:
df <- data.frame (x = c ("a", "ab", "abc", NA))
df
x
1 a
2 ab
3 abc
4
df%>% separar (x, c ("a", "b"), extra = "mesclar")%>% unite (x, a, b, sep = "")
x
1 a NA
2 ab
3 abc
4 NA NA
Mensagem de aviso:
Poucos valores em 1 local: 1
Em outras palavras, se separar e unir são complementos, um deve ser capaz de usar um deles para reverter a operação do outro.
Esta não é a solução solicitada, mas uma maneira limpa de obter o resultado desejado é:
library(tidyverse)
df <- tribble(
~ID, ~d1, ~d2,
1, "G", "G",
2, "A", "G",
3, "A", "A",
4, "G", "A",
5, NA, NA,
6, "G", "G")
df %>%
replace_na(list(d1 = "", d2 = "")) %>%
unite(new, d1, d2, remove = FALSE, sep = "")
#> # A tibble: 6 × 4
#> ID new d1 d2
#> * <dbl> <chr> <chr> <chr>
#> 1 1 GG G G
#> 2 2 AG A G
#> 3 3 AA A A
#> 4 4 GA G A
#> 5 5
#> 6 6 GG G G
Eu também sugeriria adicionar uma opção de na.rm = TRUE
para lidar com NAs. Embora a solução alternativa de @jennybc funcione para este problema em particular, ela mostrará espaços em branco e separadores quando o separador não for "".
Meu problema é o mesmo com o de @danrlu . Existe uma solução melhor e mais limpa para ignorar os NAs? Atualmente eu apenas unite
todas as colunas e então str_replace_all
NAs e separadores adjacentes com strings vazias.
Aqui está uma generalização de unite
que permite criar uma nova coluna a partir de uma função arbitrária aplicada às colunas selecionadas como faria com unite
. A função precisa retornar um vetor de caractere porque depende de pmap_chr
, mas troque pmap_*
a gosto.
`` `{r}
biblioteca (tidyverse)
df <- tribble (
~ ID, ~ d1, ~ d2, ~ d3,
1, "G", "G", "C",
2, NA, "G", "T",
3, "A", NA, "G",
4, "G", "A", "A",
5, NA, NA, NA,
6, "G", "G", "G")
combine <- função (df, col, ..., diversão, remover = TRUE) {
to_merge <- quos (...)
new_col <- quo_name (enquo (col))
merge_cols <- map_chr (to_merge, quo_name)
df <- mutate (df, !! new_col: = pmap_chr (list (!!! to_merge), fun))
if (remove) df <- select (df, -one_of (merge_cols))
df
}
combine (df, new, d1, d2, d3, fun = paste0)
`` `
Não sou um grande fã dessa interface, mas achei mais útil do que unite
algumas vezes. Eu também me peguei fazendo coisas estúpidas com esta função e sinto que é algum tipo de anti-padrão.
Se houver alguma mudança unite
interface
Não estou convencido de que unite
_deve_ funcionar como paste
, pois é uma situação muito rara quando um usuário realmente deseja transformar NA
valores em strings. Mais preocupantemente, em termos de consistência da API, separate
apresentará NA
s de uma forma que unite
não pode reverter:
library(tidyr)
example <- tibble::data_frame(x = c('foo', 'foo bar', 'foo bar baz'))
example %>% separate(x, c('foo', 'bar', 'baz'), fill = 'right') # without `fill = 'right'` same result with a message
#> # A tibble: 3 x 3
#> foo bar baz
#> * <chr> <chr> <chr>
#> 1 foo <NA> <NA>
#> 2 foo bar <NA>
#> 3 foo bar baz
example %>%
separate(x, c('foo', 'bar', 'baz'), fill = 'right') %>%
unite(x, foo:baz, sep = ' ')
#> # A tibble: 3 x 1
#> x
#> * <chr>
#> 1 foo NA NA
#> 2 foo bar NA
#> 3 foo bar baz
Se NA
s estiverem no meio das colunas que obtêm unite
d e então separate
d, então o comportamento semelhante a paste
permitiria o NA
local a ser salvo (ao custo de exigir que sejam convertidos de strings em NA
reais novamente), mas na maioria das vezes o tratamento de NA
evita que as funções sejam inversas. Tornar na.rm = TRUE
o padrão seria uma alteração importante, mas provavelmente não quebraria muito código.
Na verdade, existem duas solicitações de recursos neste tópico:
NA
, a saída será NA
NA
s.2. parece ser a opção mais útil, então vou implementá-la.
@alexpghayes o plano é extrair um auxiliar geral para transformar as funções vetorizadas que acionam muitas funções tidyr em uma versão pública
Será muito fácil (e mais rápido) se isso puder ser implementado no nível stringi, então vou deixar isso de lado até que https://github.com/gagolews/stringi/issues/289 seja resolvido.
https://github.com/gagolews/stringi/issues/289 está fechado :)
@moodymudskipper está fechado, mas não implementado em stri_paste()
.
Reexpressão mínima
library(tidyr)
df <- expand_grid(x = c("a", NA), y = c("b", NA))
unite(df, z, c("x", "y"), remove = FALSE)
#> # A tibble: 4 x 3
#> z x y
#> <chr> <chr> <chr>
#> 1 a_b a b
#> 2 a_NA a <NA>
#> 3 NA_b <NA> b
#> 4 NA_NA <NA> <NA>
Criado em 07/03/2019 pelo pacote reprex (v0.2.1.9000)
Observe que você precisará de na.rm = TRUE
(deixei o padrão como está para preservar a compatibilidade com versões anteriores, pois parece que muitas pessoas provavelmente contornaram o comportamento anterior de várias maneiras)
library(tidyr)
df <- expand_grid(x = c("a", NA), y = c("b", NA))
df %>% unite("z", x:y, na.rm = TRUE, remove = FALSE)
#> # A tibble: 4 x 3
#> z x y
#> <chr> <chr> <chr>
#> 1 a_b a b
#> 2 a a <NA>
#> 3 b <NA> b
#> 4 "" <NA> <NA>
Criado em 07/03/2019 pelo pacote reprex (v0.2.1.9000)
Olá @hadley ,
Estou tendo problemas para fazer com que na.rm = TRUE funcione dentro da função unite ().
Tentei o seguinte:
> library("tidyr")
> df <- expand.grid(x = c("a", NA), y = c("b", NA))
> df
x y
1 a b
2 <NA> b
3 a <NA>
4 <NA> <NA>
> df %>% unite("z", x:y, na.rm = TRUE, remove = FALSE)
Error: `TRUE` must evaluate to column positions or names, not a logical vector
Call `rlang::last_error()` to see a backtrace
O que me dá este erro:
Error: `TRUE` must evaluate to column positions or names, not a logical vector
Call `rlang::last_error()` to see a backtrace
Erro de rastreamento:
> rlang::last_error()
<error>
message: `TRUE` must evaluate to column positions or names, not a logical vector
class: `rlang_error`
backtrace:
1. tidyr::unite(., "z", x:y, na.rm = TRUE, remove = FALSE)
10. tidyselect::vars_select(colnames(data), ...)
11. tidyselect:::bad_calls(bad, "must evaluate to { singular(.vars) } positions or names, \\\n not { first_type }")
12. tidyselect:::glubort(fmt_calls(calls), ..., .envir = .envir)
13. tidyr::unite(., "z", x:y, na.rm = TRUE, remove = FALSE)
Call `rlang::last_trace()` to see the full backtrace
> rlang::last_trace()
x
1. \-df %>% unite("z", x:y, na.rm = TRUE, remove = FALSE)
2. +-base::withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
3. \-base::eval(quote(`_fseq`(`_lhs`)), env, env)
4. \-base::eval(quote(`_fseq`(`_lhs`)), env, env)
5. \-global::`_fseq`(`_lhs`)
6. \-magrittr::freduce(value, `_function_list`)
7. +-base::withVisible(function_list[[k]](value))
8. \-function_list[[k]](value)
9. +-tidyr::unite(., "z", x:y, na.rm = TRUE, remove = FALSE)
10. \-tidyr:::unite.data.frame(., "z", x:y, na.rm = TRUE, remove = FALSE)
11. \-tidyselect::vars_select(colnames(data), ...)
12. \-tidyselect:::bad_calls(bad, "must evaluate to { singular(.vars) } positions or names, \\\n not { first_type }")
13. \-tidyselect:::glubort(fmt_calls(calls), ..., .envir = .envir)
@kasperav você provavelmente não instalou a versão de desenvolvimento do tidyr.
@hadley você está certo! Não tenho sorte em instalar a versão dev, então vou esperar que isso seja implementado em uma versão CRAN do tidyr :)
FWIW, descobri que o comportamento em que unir assume dois valores NA e produz uma string vazia é muito confuso e inesperado. Parece claro para mim que a união de dois valores de NA deve produzir um valor de NA.
Estou supondo que isso é mais claro para as pessoas que usaram paste
muito :) Simples de consertar com na_if("")
(mas é preciso esperar que a string vazia não fosse um valor distinto significativo de _NA_character
nas colunas originais!)
Eu tenho um caso de uso em que preciso usar na.rm = TRUE
e unite
para 8 colunas. Uma das colunas é toda NA. Usar na.rm = T
com unite
parece ter um comportamento diferente quando uma das colunas é toda NA. Esse é o comportamento esperado? Devo simplesmente ignorar as colunas que são todas NA antes de usar unite
?
library(tidyr)
df_notwork <- expand_grid(x = c("a", NA), y = c(NA, NA))
df_notwork %>% unite("z", x:y, na.rm = TRUE, remove = FALSE)
# A tibble: 4 x 3
z x y
<chr> <chr> <lgl>
1 a_NA a NA
2 a_NA a NA
3 NA NA NA
4 NA NA NA
Qual versão você está usando? Não é esse o resultado que obtenho (em 1.0.2.9000)
suppressPackageStartupMessages(require(tidyverse))
df_notwork <- expand_grid(x = c("a", NA), y = c(NA, NA))
df_notwork %>% unite("z", x:y, na.rm = T, remove = FALSE)
#> # A tibble: 4 x 3
#> z x y
#> <chr> <chr> <lgl>
#> 1 "a" a NA
#> 2 "a" a NA
#> 3 "" <NA> NA
#> 4 "" <NA> NA
Criado em 25/02/2020 pelo pacote reprex (v0.3.0)
Estou usando uma versão mais recente.
packageVersion("tidyverse")
[1] ‘1.3.0’
tidyverse
é diferente de tidyr
; é uma coleção de outros pacotes reunidos para facilitar o carregamento. Portanto, ele terá uma versão diferente de todos os pacotes dentro dele. Verifique sua versão de tidyr
.
Oh, desculpe, eu vi que você estava carregando tidyverse
então presumi que essa era a versão à qual você estava se referindo. Sempre presumi que atualizar tidyverse
atualizaria os pacotes contidos nele, então normalmente apenas atualizo aquele. Acho que é uma suposição inadequada!
Mesmo com a atualização do tidyr usando a versão GitHub, ainda tenho esse problema. Talvez seja outro pacote desatualizado?
packageVersion("tidyr")
[1] ‘1.0.2.9000’
> library(tidyr)
> df_notwork <- expand_grid(x = c("a", NA), y = c(NA, NA))
> df_notwork %>% unite("z", x:y, na.rm = TRUE, remove = FALSE)
# A tibble: 4 x 3
z x y
<chr> <chr> <lgl>
1 a_NA a NA
2 a_NA a NA
3 NA NA NA
4 NA NA NA
Interessante. Não sei por que estamos obtendo resultados diferentes.
Independentemente disso, me parece que seus NA
não estão sendo removidos, apesar de na.rm = F
.
Sim, eu tentaria atualizar seus outros pacotes e ver se isso resolve o problema. Mas, como expand_grid
e unite
são de tidyr
não tenho certeza de por que esse seria o caso.
Parece que minha versão de tidyselect
estava bastante desatualizada (<1.0). Eu atualizei isso e agora está funcionando conforme o esperado.
packageVersion("tidyr")
[1] ‘1.0.2.9000’
packageVersion("tidyselect")
[1] ‘1.0.0’
library(tidyr)
df_notwork <- expand_grid(x = c("a", NA), y = c(NA, NA))
df_notwork %>% unite("z", x:y, na.rm = TRUE, remove = FALSE)
# A tibble: 4 x 3
z x y
<chr> <chr> <lgl>
1 "a" a NA
2 "a" a NA
3 "" NA NA
4 "" NA NA
Olá,
Atualizei para todas as versões mais recentes dos pacotes (tidyr 1.0.2.900, tidyselect 1.0.0) e ainda estou recebendo o mesmo erro. Eu experimentei o df_notwork de Lindsay e obtive a mesma versão que ela tinha antes das atualizações. Qualquer ajuda seria apreciada!
@anjaollodart - talvez você possa tentar atualizar pacotes adicionais dos quais tidyr
depende. É apenas um palpite, mas a necessidade de atualizar separadamente tidyselect
de tidyr
foi surpreendente para mim, então talvez haja outra dependência de pacote que tenha o mesmo problema.
Querida Lindsay,
Resolvi esse problema sozinho quando usei meu próprio quadro de dados (não aquele que
estava no exemplo). E assim que fiz isso, em 10-15 minutos, eu apaguei
o comentário porque o problema não era sobre esta função.
É estranho que o GitHub ainda envie esse comentário.
Obrigado,
Julia
Em Qui, 2 de abril de 2020 às 16h02 Lindsay (Carr) Platt <
notificaçõ[email protected]> escreveu:
@anjaollodart https://github.com/anjaollodart - talvez você possa tentar
atualizar pacotes adicionais dos quais o tidyr depende. É só um palpite,
mas a necessidade de atualizar separadamente o tidyselect do tidyr foi surpreendente para
eu, então talvez haja outra dependência de pacote que tenha o mesmo problema.-
Você está recebendo isto porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/tidyverse/tidyr/issues/203#issuecomment-607865602 ,
ou cancelar
https://github.com/notifications/unsubscribe-auth/AFUQAKM6SJFDD6MZGPN2B6DRKSLHHANCNFSM4CGRBQRQ
.
Comentários muito úteis
Esta não é a solução solicitada, mas uma maneira limpa de obter o resultado desejado é: