admin管理员组

文章数量:1305060

I have this data frame,

set.seed(124)
id <- rnorm(5, mean = 100, sd = 59)
charVar <- c("Eeny (2), meeny (10), miny (21), moe (1)",
  "Catch (112), a (2), tiger (33), by (44), the (2), toe (24)",
  NA,
  "If (2), he (33), hollers (15), let (66), him (1), go (55)",
  "Eeny (224), meeny (44), miny (50), moe (76)")
df <- data.frame(id, charVar)
df
> df
         id                                                    charVar
1  18.28083                   Eeny (2), meeny (10), miny (21), moe (1)
2 102.26107 Catch (112), a (2), tiger (33), by (44), the (2), toe (24)
3  54.98122                                                       <NA>
4 112.52606  If (2), he (33), hollers (15), let (66), him (1), go (55)
5 184.10674                Eeny (224), meeny (44), miny (50), moe (76)

I want to sort every element in the rows by the numbers with it. The expected output should look like this:

> df
         id                                                     charVar
1  18.28083                    miny (21), meeny (10), Eeny (2), moe (1)
2 102.26107 Catch (112), by (44), tiger (33), toe (24), a (2),  the (2)
3  54.98122                                                        <NA>
4 112.52606   let (66), go (55), he (33), hollers (15), If (2), him (1)
5 184.10674                 Eeny (224), moe (76), miny (50), meeny (44)

Any idea how to achive the expected result? Any help would be greatly appreciated.

I have this data frame,

set.seed(124)
id <- rnorm(5, mean = 100, sd = 59)
charVar <- c("Eeny (2), meeny (10), miny (21), moe (1)",
  "Catch (112), a (2), tiger (33), by (44), the (2), toe (24)",
  NA,
  "If (2), he (33), hollers (15), let (66), him (1), go (55)",
  "Eeny (224), meeny (44), miny (50), moe (76)")
df <- data.frame(id, charVar)
df
> df
         id                                                    charVar
1  18.28083                   Eeny (2), meeny (10), miny (21), moe (1)
2 102.26107 Catch (112), a (2), tiger (33), by (44), the (2), toe (24)
3  54.98122                                                       <NA>
4 112.52606  If (2), he (33), hollers (15), let (66), him (1), go (55)
5 184.10674                Eeny (224), meeny (44), miny (50), moe (76)

I want to sort every element in the rows by the numbers with it. The expected output should look like this:

> df
         id                                                     charVar
1  18.28083                    miny (21), meeny (10), Eeny (2), moe (1)
2 102.26107 Catch (112), by (44), tiger (33), toe (24), a (2),  the (2)
3  54.98122                                                        <NA>
4 112.52606   let (66), go (55), he (33), hollers (15), If (2), him (1)
5 184.10674                 Eeny (224), moe (76), miny (50), meeny (44)

Any idea how to achive the expected result? Any help would be greatly appreciated.

Share Improve this question edited Feb 11 at 12:09 ThomasIsCoding 103k9 gold badges36 silver badges101 bronze badges asked Feb 4 at 10:11 JontroPothonJontroPothon 6004 silver badges10 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 6

Looks like this is a follow up to your previous question. Instead of dealing with XY problem, avoid the problem from the start:

#example data
df <- data.frame(var_1 = c(10, 5, 6, 0),
                 var_2 = c(0, 0, 3, 0),
                 var_3 = c(2, 0, 9, 0))

#sort and collapse to string 
df$resString <- apply(df, 1, function(x, y = x[ x != 0 ]){
  ix <- order(y, decreasing = TRUE)
  if(length(ix)) paste(paste0(names(y)[ ix ], " (", y[ ix ], ")"), collapse = ", ")  else return(NA)
  })

#sort and keep as list column
df$resList <- apply(df[, grep("^var", colnames(df), value = TRUE) ], 1,
                    function(i) sort(i[ i != 0 ], decreasing = TRUE))

#df
#   var_1 var_2 var_3                       resString resList
# 1    10     0     2           var_1 (10), var_3 (2)   10, 2
# 2     5     0     0                       var_1 (5)       5
# 3     6     3     9 var_3 (9), var_1 (6), var_2 (3) 9, 6, 3
# 4     0     0     0                            <NA>   

As you've tagged tidyverse and data.table here are approaches using both.

tidyverse approach

Essentially we strsplit() charVar into a list-column where each element is a character vector, tidyr::unnest() into long form, extract the numbers, then dplyr::summarise() back into one row per id, where we paste() back together the values in decreasing order():

library(dplyr)
df |>
    mutate(charVar = strsplit(charVar, ", ")) |>
    tidyr::unnest(charVar) |>
    mutate(n = as.integer(gsub("\\D+", "", charVar))) |>
    summarise(
        charVar = paste(charVar[order(-n)], collapse = ", "),
        .by = id
    )


#          id                                                    charVar
# 1  18.28083                   miny (21), meeny (10), Eeny (2), moe (1)
# 2 102.26107 Catch (112), by (44), tiger (33), toe (24), a (2), the (2)
# 3  54.98122                                                         NA
# 4 112.52606  let (66), go (55), he (33), hollers (15), If (2), him (1)
# 5 184.10674                Eeny (224), moe (76), miny (50), meeny (44)

data.table approach

There is no equivalent of tidyr::unnest(). While the same results can be achieved with unlisting, here's an approach which feels more idiomatic, which modifies charVar in place:

library(data.table)
setDT(df)

df[, charVar := lapply(charVar, \(x) {
    parts <- unlist(strsplit(x, ", "))
    n <- as.integer(gsub("\\D+", "", parts))
    paste(parts[order(-n)], collapse = ", ")
})]

#           id                                                    charVar
#        <num>                                                     <list>
# 1:  18.28083                   miny (21), meeny (10), Eeny (2), moe (1)
# 2: 102.26107 Catch (112), by (44), tiger (33), toe (24), a (2), the (2)
# 3:  54.98122                                                         NA
# 4: 112.52606  let (66), go (55), he (33), hollers (15), If (2), him (1)
# 5: 184.10674                Eeny (224), moe (76), miny (50), meeny (44)

With base R, you can try

transform(
    df,
    charVar = sapply(
        strsplit(charVar, ", "),
        \(x) toString(x[order(-as.integer(gsub("\\D+", "", x)))])
    )
)

or you can use gtools::mixedsort for simpler implementation

library(gtools)
df %>%
    mutate(charVar = sapply(
        strsplit(charVar, ", "),
        \(x) toString(mixedsort(x, decreasing = TRUE))
    ))

which gives

         id                                                    charVar
1  18.28083                   miny (21), meeny (10), Eeny (2), moe (1)
2 102.26107 Catch (112), by (44), tiger (33), toe (24), a (2), the (2)
3  54.98122                                                         NA
4 112.52606  let (66), go (55), he (33), hollers (15), If (2), him (1)
5 184.10674                Eeny (224), moe (76), miny (50), meeny (44)

本文标签: rRearrage string elements by the number attached with it in rowsStack Overflow