[r] 각 행에 대해 가장 큰 값의 열 이름을 반환합니다.

저는 직원 명단이 있고 그들이 어떤 부서에 가장 자주 속해 있는지 알아야합니다. 부서 이름에 대해 직원 ID를 표로 만드는 것은 간단하지만 빈도 테이블에서 명단 수보다는 부서 이름을 반환하는 것이 더 까다 롭습니다. 아래의 간단한 예 (열 이름 = 부서, 행 이름 = 직원 ID).

DF <- matrix(sample(1:9,9),ncol=3,nrow=3)
DF <- as.data.frame.matrix(DF)
> DF
  V1 V2 V3
1  2  7  9
2  8  3  6
3  1  5  4

이제 어떻게 얻을 수 있습니까

> DF2
  RE
1 V3
2 V1
3 V2



답변

데이터를 사용하는 한 가지 옵션 (향후 참조 set.seed()를 위해 sample재현 가능한 예제를 만드는 데 사용 ) :

DF <- data.frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(9,6,4))

colnames(DF)[apply(DF,1,which.max)]
[1] "V3" "V1" "V2"

사용하는 것보다 더 빠른 솔루션 apply은 다음과 max.col같습니다.

colnames(DF)[max.col(DF,ties.method="first")]
#[1] "V3" "V1" "V2"

… 또는 ties.method중 하나가 될 수있는 곳"random" "first""last"

물론 최대 값과 동일한 두 개의 열이있는 경우 문제가 발생합니다. 일부 행에 대한 결과가 두 개 이상이므로 해당 인스턴스에서 무엇을 하려는지 잘 모르겠습니다. 예 :

DF <- data.frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(7,6,4))
apply(DF,1,function(x) which(x==max(x)))

[[1]]
V2 V3
 2  3

[[2]]
V1
 1

[[3]]
V2
 2


답변

data.table솔루션에 관심이 있다면 여기에 있습니다. 첫 번째 최대 값에 대한 ID를 얻는 것을 선호하기 때문에 약간 까다 롭습니다. 마지막 최대 값을 원하면 훨씬 쉽습니다. 그럼에도 불구하고 그렇게 복잡하지 않고 빠릅니다!

여기에 치수 데이터 (26746 * 18)가 생성되었습니다.

데이터

set.seed(45)
DF <- data.frame(matrix(sample(10, 26746*18, TRUE), ncol=18))

data.table 대답:

require(data.table)
DT <- data.table(value=unlist(DF, use.names=FALSE),
            colid = 1:nrow(DF), rowid = rep(names(DF), each=nrow(DF)))
setkey(DT, colid, value)
t1 <- DT[J(unique(colid), DT[J(unique(colid)), value, mult="last"]), rowid, mult="first"]

벤치마킹 :

# data.table solution
system.time({
DT <- data.table(value=unlist(DF, use.names=FALSE),
            colid = 1:nrow(DF), rowid = rep(names(DF), each=nrow(DF)))
setkey(DT, colid, value)
t1 <- DT[J(unique(colid), DT[J(unique(colid)), value, mult="last"]), rowid, mult="first"]
})
#   user  system elapsed 
#  0.174   0.029   0.227 

# apply solution from @thelatemail
system.time(t2 <- colnames(DF)[apply(DF,1,which.max)])
#   user  system elapsed 
#  2.322   0.036   2.602 

identical(t1, t2)
# [1] TRUE

이러한 차원의 데이터에서 약 11 배 더 빠르며 data.table확장도 꽤 좋습니다.


편집 : 최대 ID 중 하나라도 괜찮다면 :

DT <- data.table(value=unlist(DF, use.names=FALSE),
            colid = 1:nrow(DF), rowid = rep(names(DF), each=nrow(DF)))
setkey(DT, colid, value)
t1 <- DT[J(unique(colid)), rowid, mult="last"]


답변

한 가지 해결책은 모든 부서를 한 열에 넣고 다른 부서를 세고 고용주 ID (이 경우 행 번호)로 그룹화 한 다음 해당 부서로 필터링하는 것입니다. 최대 값. 이 접근 방식과의 관계를 처리하기위한 몇 가지 옵션이 있습니다.

library(tidyverse)

# sample data frame with a tie
df <- data_frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(9,6,5))

# If you aren't worried about ties:  
df %>%
  rownames_to_column('id') %>%  # creates an ID number
  gather(dept, cnt, V1:V3) %>%
  group_by(id) %>%
  slice(which.max(cnt))

# A tibble: 3 x 3
# Groups:   id [3]
  id    dept    cnt
  <chr> <chr> <dbl>
1 1     V3       9.
2 2     V1       8.
3 3     V2       5.


# If you're worried about keeping ties:
df %>%
  rownames_to_column('id') %>%
  gather(dept, cnt, V1:V3) %>%
  group_by(id) %>%
  filter(cnt == max(cnt)) %>% # top_n(cnt, n = 1) also works
  arrange(id)

# A tibble: 4 x 3
# Groups:   id [3]
  id    dept    cnt
  <chr> <chr> <dbl>
1 1     V3       9.
2 2     V1       8.
3 3     V2       5.
4 3     V3       5.


# If you're worried about ties, but only want a certain department, you could use rank() and choose 'first' or 'last'
df %>%
  rownames_to_column('id') %>%
  gather(dept, cnt, V1:V3) %>%
  group_by(id) %>%
  mutate(dept_rank  = rank(-cnt, ties.method = "first")) %>% # or 'last'
  filter(dept_rank == 1) %>%
  select(-dept_rank)

# A tibble: 3 x 3
# Groups:   id [3]
  id    dept    cnt
  <chr> <chr> <dbl>
1 2     V1       8.
2 3     V2       5.
3 1     V3       9.

# if you wanted to keep the original wide data frame
df %>%
  rownames_to_column('id') %>%
  left_join(
    df %>%
      rownames_to_column('id') %>%
      gather(max_dept, max_cnt, V1:V3) %>%
      group_by(id) %>%
      slice(which.max(max_cnt)),
    by = 'id'
  )

# A tibble: 3 x 6
  id       V1    V2    V3 max_dept max_cnt
  <chr> <dbl> <dbl> <dbl> <chr>      <dbl>
1 1        2.    7.    9. V3            9.
2 2        8.    3.    6. V1            8.
3 3        1.    5.    5. V2            5.


답변

위의 제안에 따라 다음 data.table솔루션이 매우 빠르게 작동했습니다.

library(data.table)

set.seed(45)
DT <- data.table(matrix(sample(10, 10^7, TRUE), ncol=10))

system.time(
  DT[, col_max := colnames(.SD)[max.col(.SD, ties.method = "first")]]
)
#>    user  system elapsed 
#>    0.15    0.06    0.21
DT[]
#>          V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 col_max
#>       1:  7  4  1  2  3  7  6  6  6   1      V1
#>       2:  4  6  9 10  6  2  7  7  1   3      V4
#>       3:  3  4  9  8  9  9  8  8  6   7      V3
#>       4:  4  8  8  9  7  5  9  2  7   1      V4
#>       5:  4  3  9 10  2  7  9  6  6   9      V4
#>      ---                                       
#>  999996:  4  6 10  5  4  7  3  8  2   8      V3
#>  999997:  8  7  6  6  3 10  2  3 10   1      V6
#>  999998:  2  3  2  7  4  7  5  2  7   3      V4
#>  999999:  8 10  3  2  3  4  5  1  1   4      V2
#> 1000000: 10  4  2  6  6  2  8  4  7   4      V1

또한 .SD다음에서 언급하여 고려해야 할 열을 항상 지정할 수있는 이점이 있습니다 .SDcols.

DT[, MAX2 := colnames(.SD)[max.col(.SD, ties.method="first")], .SDcols = c("V9", "V10")]

@lwshang이 제안한 것처럼 가장 작은 값의 열 이름이 필요한 경우 -.SD다음 을 사용해야합니다 .

DT[, col_min := colnames(.SD)[max.col(-.SD, ties.method = "first")]]


답변

dplyr솔루션 :

생각:

  • rowid를 열로 추가
  • 긴 형식으로 모양 변경
  • 각 그룹의 최대 필터

암호:

DF = data.frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(9,6,4))
DF %>%
  rownames_to_column() %>%
  gather(column, value, -rowname) %>%
  group_by(rowname) %>%
  filter(rank(-value) == 1)

결과:

# A tibble: 3 x 3
# Groups:   rowname [3]
  rowname column value
  <chr>   <chr>  <dbl>
1 2       V1         8
2 3       V2         5
3 1       V3         9

이 접근 방식은 상단 n열 을 얻기 위해 쉽게 확장 할 수 있습니다. 예 n=2:

DF %>%
  rownames_to_column() %>%
  gather(column, value, -rowname) %>%
  group_by(rowname) %>%
  mutate(rk = rank(-value)) %>%
  filter(rk <= 2) %>%
  arrange(rowname, rk)

결과:

# A tibble: 6 x 4
# Groups:   rowname [3]
  rowname column value    rk
  <chr>   <chr>  <dbl> <dbl>
1 1       V3         9     1
2 1       V2         7     2
3 2       V1         8     1
4 2       V3         6     2
5 3       V2         5     1
6 3       V3         4     2


답변

간단한 for루프도 유용 할 수 있습니다.

> df<-data.frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(9,6,4))
> df
  V1 V2 V3
1  2  7  9
2  8  3  6
3  1  5  4
> df2<-data.frame()
> for (i in 1:nrow(df)){
+   df2[i,1]<-colnames(df[which.max(df[i,])])
+ }
> df2
  V1
1 V3
2 V1
3 V2


답변

한 가지 옵션 dplyr 1.0.0은 다음과 같습니다.

DF %>%
 rowwise() %>%
 mutate(row_max = names(.)[which.max(c_across(everything()))])

     V1    V2    V3 row_max
  <dbl> <dbl> <dbl> <chr>
1     2     7     9 V3
2     8     3     6 V1
3     1     5     4 V2

샘플 데이터 :

DF <- structure(list(V1 = c(2, 8, 1), V2 = c(7, 3, 5), V3 = c(9, 6,
4)), class = "data.frame", row.names = c(NA, -3L))