प्रत्येक पंक्ति के लिए सबसे बड़े मान का कॉलम नाम लौटाएं


97

मेरे पास कर्मचारियों का रोस्टर है, और मुझे यह जानने की जरूरत है कि वे किस विभाग में हैं। विभाग के नाम के खिलाफ कर्मचारी आईडी को सारणीबद्ध करना तुच्छ है, लेकिन आवृत्ति तालिका से रोस्टर गणना की संख्या के बजाय विभाग का नाम वापस करना मुश्किल है। नीचे एक सरल उदाहरण (कॉलम नाम = विभाग, पंक्ति नाम = कर्मचारी आईडी)।

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

आपका वास्तविक डेटा कितना बड़ा है?
अरुण

1
@Arun> मंद (परीक्षण) [1] 26,746 18
dmvianna

6
एक दिलचस्प सामान्यीकरण प्रति पंक्ति में सबसे बड़ा n मानों का कॉलम नाम होगा
Hack-R

जवाबों:


99

आपके डेटा का उपयोग करने वाला एक विकल्प (भविष्य के संदर्भ के लिए, प्रतिलिपि प्रस्तुत करने 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 

अगर मेरे पास दो बराबर कॉलम हैं, तो मैं आमतौर पर पहले उठाता हूं। ये सीमा मामले हैं जो मेरे सांख्यिकीय विश्लेषण को परेशान नहीं करते हैं।
dmvianna

1
@ मद्धिमना - उपयोग which.maxकरना तब ठीक होगा।
Thelatemail

मैं मान रहा हूं कि आदेश संरक्षित है, इसलिए मैं इस वेक्टर के साथ एक नया कॉलम बना सकता हूं जो कर्मचारियों के आईडी में सही ढंग से संरेखित होगा। क्या वो सही है?
dmvianna

applyआंतरिक रूप data.frameसे धर्मान्तरित matrix। आप इन आयामों पर प्रदर्शन अंतर नहीं देख सकते हैं।
अरुण

2
@PankajKaundal - अलग-अलग मूल्यों को मानते हुए, इस बारे मेंcolnames(DF)[max.col(replace(DF, cbind(seq_len(nrow(DF)), max.col(DF,ties.method="first")), -Inf), "first")]
thelatemail

15

यदि आप एक data.tableसमाधान में रुचि रखते हैं, तो यहां एक है। यह थोड़ा मुश्किल है क्योंकि आप पहले अधिकतम के लिए आईडी प्राप्त करना पसंद करते हैं। यदि आप अंतिम अधिकतम चाहते हैं तो यह बहुत आसान है। फिर भी, यह उतना जटिल नहीं है और यह तेज़ है!

यहां मैंने आपके आयामों (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बहुत अच्छी तरह से तराजू भी।


संपादित करें: यदि कोई अधिकतम आईडी ठीक है, तो:

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"]

अगर यह पहली या आखिरी अधिकतम है तो मुझे वास्तव में परवाह नहीं है। मैं पहले सादगी के लिए जा रहा हूं, लेकिन मुझे यकीन है कि भविष्य में डेटाटेबल समाधान काम आएगा, धन्यवाद!
dmvianna

11

एक समाधान यह हो सकता है कि सभी विभागों को एक कॉलम में रखकर लंबे समय से तारीख को फिर से चलाएं, नियोक्ता आईडी द्वारा समूह (इस मामले में, पंक्ति संख्या), और फिर विभाग (एस) के साथ फ़िल्टर करें अधिकतम मूल्य। इस दृष्टिकोण के साथ संबंधों को संभालने के लिए कुछ विकल्प हैं।

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.

11

उपरोक्त सुझावों के आधार पर, निम्न 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")]

मामले में हमें सबसे छोटे मूल्य के कॉलम नाम की आवश्यकता है, जैसा कि @lhanghang द्वारा सुझाया गया है, एक को बस उपयोग करने की आवश्यकता है -.SD:

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

मुझे एक समान आवश्यकता थी, लेकिन प्रत्येक पंक्ति के लिए न्यूनतम मान वाले स्तंभ नाम को प्राप्त करना चाहते हैं ..... हमें R में min.col नहीं लगता है। क्या आप जानते हैं कि इसके समकक्ष समाधान क्या होगा? ?
user1412

हाय @ user1412। आपके दिलचस्प सवाल के लिए धन्यवाद। मेरे पास अभी ऐसा कुछ भी नहीं है जो किसी which.minऐसी चीज़ का उपयोग करने के अलावा हो, जो इस तरह दिखेगी : DT[, MIN := colnames(.SD)[apply(.SD,1,which.min)]]या DT[, MIN2 := colnames(.SD)[which.min(.SD)], by = 1:nrow(DT)]ऊपर के डमी डेटा पर। यह संबंधों पर विचार नहीं करता है और केवल पहले न्यूनतम रिटर्न देता है। शायद एक अलग सवाल पूछने पर विचार करें। मुझे उत्सुकता होगी कि आपको अन्य उत्तर क्या मिलेंगे।
वैलेंटाइन

1
न्यूनतम कॉलम प्राप्त करने की एक ट्रिक, data. negative को अधिकतम .col में भेज रही है, जैसे colnames(.SD)[max.col(-.SD, ties.method="first")]:।
लावाशांग

6

एक dplyrसमाधान:

विचार:

  • एक स्तंभ के रूप में पंक्तिबद्ध जोड़ें
  • लंबे प्रारूप में फेरबदल करें
  • प्रत्येक समूह में अधिकतम के लिए फ़िल्टर

कोड:

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

1
क्या आप इस दृष्टिकोण और उप-उत्तर के ऊपर के अंतर पर टिप्पणी कर सकते हैं? वे मेरे लिए उसी के बारे में देखते हैं।
ग्रेगर थॉमस

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

1

से एक विकल्प हो 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))

0

यहाँ एक उत्तर है जो data.table के साथ काम करता है और सरल है। यह आपका डेटा मानता है। नाम दिया गया है yourDF:

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

बदलें ("V1", "V2", "V3", "V4")और (V1, V2, V3, V4)अपने कॉलम नामों के साथ

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.