【问题标题】:Using R to create a new dataframe using tests from values in another dataframe使用 R 使用来自另一个数据帧中的值的测试来创建新数据帧
【发布时间】:2019-11-20 08:32:41
【问题描述】:

我有以下(示例)格式的销售订单数据:

salesorder <- c('TM001', 'TM002', 'TM003', 'TM004')
esttxndate <- as.Date(c('2018-10-01', '2018-10-01', '2018-10-04', '2018-10-06'))
potxndate <- as.Date(c('2018-10-07', '2018-10-06', '2018-10-14', '2018-10-18'))
intxndate <- as.Date(c('2018-11-06', '2018-11-05', '2018-11-13', '2018-11-17'))
salesorder <- data.frame(salesorder, esttxndate, potxndate, intxndate)
 salesorder esttxndate  potxndate   intxndate
1   TM001   2018-10-01  2018-10-07  2018-11-06  
2   TM002   2018-10-01  2018-10-06  2018-11-05  
3   TM003   2018-10-04  2018-10-14  2018-11-13  
4   TM004   2018-10-06  2018-10-18  2018-11-17

我正在尝试创建一个新的数据框,它查看每个销售订单的日期并输出每个日期的状态:

     date       TM001   TM002   TM003   TM004
01 2018-10-01    est     est     dne      dne
02 2018-10-02    est     est     dne      dne
.
07 2018-10-07    pro     pro     est      est
.
32 2018-11-01    pro     pro     pro      pro
.
37 2018-11-06    inv     inv     pro      pro
.
48 2018-11-17    inv     inv     inv      inv

我能够使用 min 和 max 函数(保存为 mindate 和 maxdate)获取日期列表。然后,我开始了一个新的 data.frame,其日期范围为:

mindate <- min(esttxndate, potxndate, intxndate)
maxdate <- max(esttxndate, potxndate, intxndate)
dates <- data.frame(as.Date(as.Date(mindate):as.Date(maxdate), origin="1970-01-01"))
names(dates)[1] <- "date"

由于我试图利用用户定义的函数并在新创建的日期数据框和以前的 salesorder 数据框上跨行应用,所以我不知道下一步该做什么。

我来自 Stata 的背景,并且能够通过首先检查并保存每个日期的温度值来生成所需的数据集(例如局部变量 potxndate_TM001 = 2018-10-07)

ds *date
foreach dt in `r(varlist)' {
    forval i = 1/`=_N' {
        local so = salesorder[`i']
        local `dt'_`so' = `dt'[`i']
    }
}

将所有日期保存为局部变量后,我删除了除 salesorder 之外的所有变量,转置表格并创建了一个新的变量 date,范围从最小日期到最大日期。然后,我运行以下命令以根据日期列和本地保存的变量获取值。

ds TM*
foreach so in `r(varlist)' {
    forval i = 1/`=_N' {
        if `intxndate_`so'' <= date[`i'] {
            replace `so' = "inv" in `i'
        }
        else if `potxndate_`so'' <= date[`i'] {
            replace `so' = "pro" in `i'
        }
        else if `esttxndate_`so'' <= date[`i'] {
            replace `so' = "est" in `i'
        }
        else if `esttxndate_`so'' > date[`i'] {
            replace `so' = "dne" in `i'
        }
    }
}

我相信有一种方法可以在 R 中做到这一点,而无需创建中间局部变量/修改原始数据集,这应该会更高效、更快 (?)。

【问题讨论】:

  • 您好,您能解释一下 esttxndate、potxndate 和 intxndate 的含义以及我如何从中获取状态吗? lubridate 中的int_overlaps 函数(和其他函数)在这里可能很有用。
  • @MartijnVanAttekum 如果日期早于 esttxndate,则销售订单为“dne”(不存在)如果日期在 esttxndate 或之后但在 potxndate 之前,则销售订单为“est”或如果日期在 potxndate 或之后但在 intxndate 之前,则估计销售订单为“pro”或生产中,如果日期在 intxndate 或之后,则销售订单为“inv”或正在开票
  • Stata 代码可以重写,我怀疑,以减少大部分循环。如果您对此感兴趣,请发布一个新问题,但请按照 Stata 标签 wiki 以可读形式提供示例数据。

标签: r date dataframe apply stata


【解决方案1】:

R 中的循环往往很慢,一个更快的解决方案是使用函数式编程工具,例如 purrr 包或函数 apply 中的工具,而不是您在 stata 中使用的循环。

为了解决这个问题,我编写了自己的函数most_recent_txn,它返回给定日期的给定销售订单的状态,然后使用purrr::map_chr() 将此函数应用于向量dates$date 中的所有日期。

然后,为了对所有销售订单(原始数据框中的行,salesorder)执行此操作,我编写了一个函数,对给定的行执行此操作,并使用 apply 函数将其应用于所有行。


most_recent_txn <- function(as_of_date, order_dates) {
  # return the column name of the last txn step compleated, as of the date given.
  last_step = "dne"

  # if there is any recorded activity at that point, we assign the most recent
  # activity to last step
  if(max(which(t(order_dates)<=as_of_date))>0){
    last_step = names(order_dates)[max(which(t(order_dates)<=as_of_date))]
  }
  return(last_step)
}


progress_of_sales_order <- function(order) {
   purrr::map_chr(dates$date,most_recent_txn,order_dates=order[2:4])
}

status = cbind(dates$date,
          apply(salesorder,1,FUN=progress_of_sales_order))

不是最优雅的解决方案,它依赖于使用salesorder 数据框的列顺序来指示流程中的步骤,并隐含地假设销售订单的状态不能倒退(例如,发票后的采购订单)。

【讨论】:

    【解决方案2】:

    创建了一个名为 status 的函数

    status <- function(tstdate, estdate, podate, invdate) {
       ifelse (tstdate >= invdate, "inv",
          ifelse (tstdate >= podate, "pro",
             ifelse (tstdate >= estdate, "est", "dne")))
    }
    

    然后我可以跨日期运行以下命令:

    final <- data.frame(apply(dates,c(1,2),function(x) {
       status(x, salesorder$esttxndate, salesorder$potxndate, salesorder$intxndate)
    }
    

    剩下的就是格式化以使数据框符合我的喜好。

    次要注意:此解决方案的行和列将与问题针对所需结果的建议方式相反。这种方式的问题是无法命名列日期,因为数字不能在名称中使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-20
      • 2015-12-31
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      • 2018-12-25
      相关资源
      最近更新 更多