Programing

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

lottogame 2020. 10. 22. 07:36
반응형

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


저는 직원 명단이 있으며 그들이 가장 자주 근무하는 부서를 알아야합니다. 부서 이름에 대해 직원 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"]

위의 제안에 따라 다음 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")]]

한 가지 해결책은 모든 부서를 한 열에 넣고 다른 부서를 세고 고용주 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.

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

Here is an answer that works with data.table and is simpler. This assumes your data.table is named yourDF:

j1 <- max.col(yourDF[, .(V1, V2, V3, V4)], "first")
yourDF$newCol <- c("V1", "V2", "V3", "V4")[j1]

Replace ("V1", "V2", "V3", "V4") and (V1, V2, V3, V4) with your column names

참고URL : https://stackoverflow.com/questions/17735859/for-each-row-return-the-column-name-of-the-largest-value

반응형