Como selecionar entre 3 valores, os 2 mais próximos um do outro em R?


8

Gostaria de selecionar para cada um IDdos dois valores mais próximos de Cq. Eu pensei que tinha descoberto, mas depende da posição da linha ...

Aqui está um exemplo da forma do meu conjunto de dados:

df <- data.frame(ID = c("A","A","A","B","B","B","C","C","C"), 
                 Cq = c(34.32,34.40,34.31,31.49,31.40,31.49,31.22,31.31,31.08))
  ID    Cq
1  A 34.32
2  A 34.40
3  A 34.31
4  B 31.49
5  B 31.40
6  B 31.49
7  C 31.22
8  C 31.31
9  C 31.08

E o que eu tentei

df4 <-df %>% 
  group_by(ID) %>% 
  arrange(Cq) %>% 
  mutate(diffvals= Cq - lag(Cq)) %>%
  filter(row_number() == 1 | row_number() == 2)

#Output
ID       Cq   diffvals
1 A      34.31   NA     
2 A      34.32   0.0100
3 B      31.40   NA     
4 B      31.49   0.0900
5 C      31.08   NA     
6 C      31.22   0.14 

E a saída esperada

 ID    Cq
1  A 34.32
2  A 34.31
3  B 31.49
4  B 31.49
5  C 31.22
6  C 31.31

Eu tentei classificar meu conjunto de dados antes, mas isso não muda nada. Eu também tentei usar, filter(diffvals=wich.min==diffvals)mas não sei como extrair os dois menores.

Se você tem alguma idéia, isso me ajudaria muito!

desde já, obrigado

Respostas:


4

Aqui está um código R básico, onde disté usado para enumerar distâncias de todos os pares dentro de grupos, ou seja,

dfout <- do.call(rbind,
                 lapply(split(df,df$ID), 
                        function(v) {
                          d <- `diag<-`(as.matrix(dist(v$Cq)),NA)
                          d[lower.tri(d)] <- NA
                          v[which(d==min(d,na.rm = T),arr.ind = T),]
                        }
                 ))

de tal modo que

> dfout
    ID    Cq
A.1  A 34.32
A.3  A 34.31
B.4  B 31.49
B.6  B 31.49
C.7  C 31.22
C.8  C 31.31

3

Usar dplyruma opção é fazer um full_joincom itselfbase em ID. Remova as linhas geradas em combinação consigo mesma e, para cada uma, IDselecione a linha com diferença mínima e obtenha os dados em formato longo.

library(dplyr)

df %>%
  mutate(Row = row_number()) %>%
  full_join(df, by = 'ID') %>%
  group_by(ID, Row) %>%
  filter(Cq.x != Cq.y) %>%
  group_by(ID) %>%
  slice(which.min(abs(Cq.x - Cq.y))) %>%
  tidyr::pivot_longer(cols  = starts_with('Cq')) %>%
  select(-Row, -name)

#  ID    value
#  <fct> <dbl>
#1 A      34.3
#2 A      34.3
#3 B      31.5
#4 B      31.4
#5 C      31.2
#6 C      31.3

1

Tente o seguinte:

library(tidyverse)
df <- data.frame(ID = c("A","A","A","B","B","B","C","C","C"), 
                 Cq = c(34.32,34.40,34.31,31.49,31.40,31.49,31.22,31.31,31.08))

df_summ <- 
  df %>% 
  group_by(ID) %>% 
  arrange(Cq) %>% 
  mutate(
    prev = lag(Cq),
    diff= Cq - lag(Cq)) %>% 
  drop_na()
df_summ %>% 
  group_by(ID) %>% 
  summarise(diff = min(diff)) %>% 
  left_join(df_summ) %>% 
  select(-diff) %>% 
  pivot_longer(c(Cq, prev), values_to = "cq") %>% 
  select(-name)

Cumprimentos Paweł


1

Na base R

do.call(rbind, lapply(split(df, df$ID), function(x){ 
  cell <- order(abs(outer(x$Cq, x$Cq, `-`)))[-seq(nrow(x))][1] - 1;
  x[c((cell %/% nrow(x)) + 1, (cell %% nrow(x)) + 1),]}))
#>     ID    Cq
#> A.1  A 34.32
#> A.3  A 34.31
#> B.4  B 31.49
#> B.6  B 31.49
#> C.7  C 31.22
#> C.8  C 31.31

0

Saída diferente, mas funcionalmente equivalente

do.call(rbind,
  by(df,list(df$ID),function(x){
    tmp=abs(outer(x$Cq,x$Cq,"-"))
    tmp[upper.tri(tmp,diag=T)]=Inf
    x$Cq[which(tmp==min(tmp),arr.ind=T)]
  })
)

   [,1]  [,2]
A 34.31 34.32
B 31.49 31.49
C 31.31 31.22
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.