替换数字列中的除以符号

Replace divided by symbol in numeric column

我有一个包含以下内容的数据框:

df$old_price <- c('SR 2356' , 'SR 785' , 'SR 50/4 pack', 'SR 10/4 pack,'SR 490')

如何替换 old_price 列中的值,其中 'SR 50/4 pack' 或 'SR 10/4 包等值分别给出 12.5 和 2.5 而不会损坏数据?

我试过了df$old_price <- as.integer(gsub('[a-zA-Z]', '', df$old_price))。但是,它似乎创建了奇怪的列值。

要获取带有除号 / 的字符的数值,另一种方法是拆分字符,以便提取不带 / 的数字。之后将提取出来的数字转为数值,然后用/.

对数字进行除法
# Define the function
getNumber <- function(string_vect){
   extracted_number <- gsub(".*?([0-9/0-9]+).*","\1", string_vect)
   split_number <- strsplit(extracted_number, "/") |> unlist() |> as.numeric()
   divided_number <- split_number[1]/split_number[2]
   return(divided_number)
}
#Apply the function to the column
mydf <- data.frame(price = c("SR 50/4 pack", "SR 10/4"))

lapply(mydf$price, getNumber) |> unlist()
#[1] 12.5  2.5

如果该列包含混合字符,其中一些带有/,而另一些则没有,可以使用条件ifelse修改函数,如下所示:

 getAllnumber <- function(string_vect){
     extracted_number <- gsub(".*?([0-9/0-9]+).*","\1", string_vect)
     if(grepl("/", string_vect)){
     split_number <- strsplit(extracted_number, "/") |> unlist() |> as.numeric()
     resulted_number <- split_number[1]/split_number[2]
     }
     else{
         resulted_number <- extracted_number |> as.numeric()
     }
     return(resulted_number)
 }

#apply the function to the column

mydf <- data.frame(price = c("SR 2356","SR 785","SR 50/4 pack",
                            "SR 10/4 pack","SR 490"))

lapply(mydf$price, getAllnumber) |> unlist()
#[1] 2356.0  785.0   12.5    2.5  490.0

# or 
vapply(mydf$price, getAllnumber, numeric(1))
#     SR 2356      SR 785 SR 50/4 pack   SR 10/4 pack    SR 490 
#     2356.0        785.0         12.5            2.5     490.0 

编辑以显示没有管道的相同函数

无需使用任何 |> 管道即可定义完全相同的函数,如下所示。

getAllnumber2 <- function(string_vect){
     extracted_number <- gsub(".*?([0-9/0-9]+).*","\1", string_vect)
     if(grepl("/", string_vect)){
     split_number <- as.numeric(unlist(strsplit(extracted_number, "/")))
     resulted_number <- split_number[1]/split_number[2]
     }
     else{
         resulted_number <- as.numeric(extracted_number)
     }
     return(resulted_number)
 }

mydf <- data.frame(price = c("SR 2356","SR 785","SR 50/4 pack",
                            "SR 10/4 pack","SR 490"))

unlist(lapply(mydf$price, getAllnumber2))

此代码将 return 与管道函数的结果相同:

#[1] 2356.0  785.0   12.5    2.5  490.0

不确定这有多稳定,但你可以试试

library(stringr)
library(dplyr)

df %>% 
  mutate(new.price = as.integer(str_extract(old.price, "\d+(?=/|$)")) / coalesce(as.integer(str_extract(old.price, "(?<=/)\d+")), 1))

这个returns

     old.price new.price
1      SR 2356    2356.0
2       SR 785     785.0
3 SR 50/4 pack      12.5
4 SR 10/4 pack       2.5
5       SR 490     490.0

这可能是另一个解决方案:

library(stringr)

unlist(lapply(str_extract(vec, "\d.*\d"), \(x) eval(parse(text = x))))

[1] 2356.0  785.0   12.5    2.5  490.0

亲爱的 Ian Campbell:

建议的替代正则表达式解决方案
unlist(lapply(str_extract(vec, "[\d,./]+"), \(x) eval(parse(text = x))))

这是一个使用正则表达式与 stringr 中的 str_match 匹配的解决方案。

#sample data
input <- structure(list(item_id = 1:5, 
                    price = c(265L, 995L, 20L, 7L, 421L), 
                    old_price = c("105", "No old price", "SR 50/4 pack", "SR 10/4 pack", "520")), 
                    class = "data.frame", row.names = c(NA, -5L))

# item_id price    old_price
# 1       265      105
# 2       995      No old price
# 3       20       SR 50/4 pack
# 4       7        SR 10/4 pack
# 5       421      520

正则表达式在进行匹配时对每个值进行分组(例如 50/4)。然后我使用循环来识别匹配的记录并使用新值更新记录。

for(i in 1:nrow(input)) {
  x <- as.numeric(str_match(input[i,]$old_price, "(\d+)\/(\d+)")[,2]) 
  y <- as.numeric(str_match(input[i,]$old_price, "(\d+)\/(\d+)")[,3])
  
    if(!is.na(x) && !is.na(y)) {
      input[i,]$old_price <- x / y
    }
  
}

# item_id price    old_price
# 1       265      105
# 2       995      No old price
# 3       20       12.5
# 4       7        2.5
# 5       421      520