【问题标题】:Get the limited rows on date range from dataframe in R从 R 中的数据框中获取日期范围内的有限行
【发布时间】:2018-02-20 06:53:13
【问题描述】:

我有这个数据框。

token    DD1                   Type         DD2         Price
AB-1     2018-01-01 10:12:15   Low          2018-01-25  10000
AB-5     2018-01-10 10:12:15   Low          2018-01-25  15000
AB-2     2018-01-05 12:25:04   High         2018-01-20  25000
AB-3     2018-01-03 17:04:25   Low          2018-01-27  50000
....
AB-8     2017-12-10 21:08:12   Low          2017-12-30  60000
AB-8     2017-12-10 21:08:12   High         2017-12-30  30000

输入:

structure(list(token = structure(c(2L, 5L, 3L, 4L, 1L, 6L, 6L
), .Label = c("....", "AB-1", "AB-2", "AB-3", "AB-5", "AB-8"), class = "factor"), 
    DD1 = structure(c(2L, 5L, 4L, 3L, 1L, 6L, 6L), .Label = c("", 
    "01/01/2018 10:12:15", "03/01/2018 17:04:25", "05/01/2018 12:25:04", 
    "10/01/2018 10:12:15", "10/12/2017 21:08:12"), class = "factor"), 
    Type = structure(c(3L, 3L, 2L, 3L, 1L, 3L, 2L), .Label = c("", 
    "High", "Low"), class = "factor"), DD2 = structure(c(3L, 
    3L, 2L, 4L, 1L, 5L, 5L), .Label = c("", "20/01/2018", "25/01/2018", 
    "27/01/2018", "30/12/2017"), class = "factor"), Price = c(10000L, 
    15000L, 25000L, 50000L, NA, 60000L, 30000L)), .Names = c("token", 
"DD1", "Type", "DD2", "Price"), class = "data.frame", row.names = c(NA, 
-7L))

从上面提到的数据框中,如果行对于特定日期不可用,我想要基于日期的 2 种子集数据框(最后三个日期按降序排列(来自DD2),而不是将该日期显示为所有字段为' 0') 和月份(如果行对于特定日期不可用,则按降序排列最后三个日期,而不是将该日期显示为所有字段为 '0')。

Avg Low 公式(Avg High 相同):DD2-DD1,然后根据可用的 nrow 取中值。

月份百分比公式:(最近值-旧值)/(旧值)

每当我运行代码时,代码应该从数据框中选择最近三天的数据以及最近三个月的数据。

DF1:

Date        nrow for Low  Med Low sum of value low nrow for High  Med High sum of value High
27-01-2018  1             24      50000            0             0          0
26-01-2018  0             0       0                0             0          0
25-01-2018  2             19.5    25000            0             0          0

DF2

Month  nrow low    %    sum low     %    nrow high     %     sum high     % 
Jan-18 3         200%   75000     25%    1            0%     25000     -17%
Dec-17 1         100%   60000    100%    1          100%     0         100%
Nov-17 0          -     -        -       0           -       -         -

【问题讨论】:

  • @ZahiroMor 这是我想要的输出,没有存储为 R 中的 Dataframe ...:(
  • 如果您还可以展示您迄今为止尝试过的内容以及您卡在哪里,您将获得更多帮助
  • 没有数据困难,但这可能是一种方法,使用dplyr:您需要一个包含一年中所有日期的列的数据框。将它与您的数据框连接起来,这样您就可以确定没有数据的日子也连续。您必须将日期文件存储为日期(使用lubridate)。然后您可以轻松地arrange 按降序排列,group_by 月份,使用包含所需计算的mutate 创建新列,然后filter 每月获取(第一)3 行。
  • @ZahiroMor 更新了输入数据。
  • @Tjebo 我正在尝试使用该方法但还无法完成DF1$Sum_low<-paste('USD ',formatC(sum(df[df1$TYPE=='Low' & & !(df$token%in% price$token) ,]$price), big.mark=',', format = 'f', digit=1))

标签: r dataframe matrix dplyr data.table


【解决方案1】:

虽然这个 Q 已经有一个公认的答案,但我觉得很难提供一个使用 dcast()melt() 的答案。使用CJ() 完成任何缺失的日期和月份,并按照 OP 的要求加入。

代码试图尽可能接近地重现 OP 的预期结果。特定的自定义是代码看起来如此复杂的原因。

如果需要,我愿意更详细地解释代码。

library(data.table)
setDT(DF)

# daily
DF1 <- 
  DF[, .(n = .N, days = median(difftime(as.Date(DD2, "%d/%m/%Y"), 
                                        as.Date(DD1, "%d/%m/%Y"), units = "day")), 
         sum = sum(Price)), by = .(DD2, Type)][
           , Date := as.Date(DD2, "%d/%m/%Y")][
             , dcast(.SD, Date ~ Type, value.var = c("n", "days", "sum"), fill = 0)][
               .(Date = seq(max(Date), length.out = 3L, by = "-1 days")), on = "Date"][
                 , setcolorder(.SD, c(1, 3, 5, 7, 2, 4, 6))][
                   is.na(n_Low), (2:7) := lapply(.SD, function(x) 0), .SDcols = 2:7][]
DF1
         Date n_Low  days_Low sum_Low n_High days_High sum_High
1: 2018-01-27     1 24.0 days   50000      0    0 days        0
2: 2018-01-26     0  0.0 days       0      0    0 days        0
3: 2018-01-25     2 19.5 days   25000      0    0 days        0
# monthly
DF2 <-
  DF[, Month := lubridate::floor_date(as.Date(DD2, "%d/%m/%Y"), unit = "month")][
    , .(n = .N, sum = sum(Price)), by = .(Month, Type)][
      CJ(Month = seq(max(Month), length.out = 3L, by = "-1 months"), Type = unique(Type)), 
      on = .(Month, Type)][
        , melt(.SD, id.vars = c("Month", "Type"))][
          is.na(value), value := 0][
            , Pct := {
              old <- shift(value); round(100 * ifelse(old == 0, 1, (value - old) / old))
            }, 
            by = .(variable, Type)][
              , dcast(.SD, Type + Month ~ variable, value.var = c("value", "Pct"))][
                , setnames(.SD, c("value_n", "value_sum"), c("n", "sum"))][
                  , dcast(.SD, Month ~ Type, value.var = c("n", "Pct_n", "sum", "Pct_sum"))][
                    order(-Month), setcolorder(.SD, c(1, 3, 5, 7, 9, 2, 4, 6, 8))]
DF2
        Month n_Low Pct_n_Low sum_Low Pct_sum_Low n_High Pct_n_High sum_High Pct_sum_High
1: 2018-01-01     3       200   75000          25      1          0    25000          -17
2: 2017-12-01     1       100   60000         100      1        100    30000          100
3: 2017-11-01     0        NA       0          NA      0         NA        0           NA

【讨论】:

  • 我还能在 DF2 2 中为每个 LowHigh 获得 4 个额外的列,它们分别给我 Med for LowAverage 的总和或低和高。特定月份。
  • @Uwe 很有趣。不幸的是,由于我没有使用 data.table,因此我无法提供其中的部分答案。但是,是的。我也想过使用 dcast,但没有弄清楚如何做到这一点,这看起来确实不错:)
【解决方案2】:

以下方法有帮助吗?

require(tidyverse)

编辑 这是一种非常复杂的方法,并且肯定可以更优雅地解决。

dat <- structure(list(token = structure(c(2L, 5L, 3L, 4L, 1L, 6L, 6L), .Label = c("....", "AB-1", "AB-2", "AB-3", "AB-5", "AB-8"), class = "character"), DD1 = structure(c(2L, 5L, 4L, 3L, 1L, 6L, 6L), .Label = c("", "01/01/2018 10:12:15", "03/01/2018 17:04:25", "05/01/2018 12:25:04", "10/01/2018 10:12:15", "10/12/2017 21:08:12"), class = "factor"),
Type = structure(c(3L, 3L, 2L, 3L, 1L, 3L, 2L), .Label = c("", "High", "Low"), class = "character"), DD2 = structure(c(3L, 3L, 2L, 4L, 1L, 5L, 5L), .Label = c("", "20/01/2018", "25/01/2018", "27/01/2018", "30/12/2017"), class = "factor"), Price = c(10000L, 15000L, 25000L, 50000L, NA, 60000L, 30000L)), .Names = c("token", "DD1", "Type", "DD2", "Price"), class = "data.frame", row.names = c(NA, -7L))
#I have included this into the code because structure(your output) had messed up a lot with factors   

dat <- dat[c(1:4,6:7),]
dat <- dat %>% mutate(DD1 = dmy_hms(DD1), DD2 = dmy(DD2), Type = as.character(Type))

dat_summary <- dat %>%  
 mutate(diff_days = round(as.duration(DD1%--%DD2)/ddays(1),0),
#uses lubridate  to calculate the number of days between each DD2 and DD1 
 n = n()) %>% 
 group_by(DD2,Type) %>% #because your operations are performed by each Type by DD2
 summarise(med  = median(diff_days),# calculates the median
           sum = sum(Price)) # and the sum

# A tibble: 5 x 4
# Groups:   DD2 [?]
  DD2        Type    med   sum
  <date>     <chr> <dbl> <int>
1 2017-12-30 2      19.0 30000
2 2017-12-30 3      19.0 60000
3 2018-01-20 2      14.0 25000
4 2018-01-25 3      19.5 25000
5 2018-01-27 3      23.0 50000 

现在找到价格为值的第一天

 datematch <- dat %>% group_by(Type,month = floor_date(DD2, "month")) %>%
      arrange(Type, desc(DD2)) %>%
      summarise(maxDate = max(DD2)) %>% 
      select(Type, maxDate)

现在创建用于合并的辅助数据框。 dummy_dates 将包含带有值的最后一天和前两天,对于两种类型(低和高),all_dates 将包含......好吧,所有日期

list1 <- split(datematch$maxDate, datematch$Type)
list_type2 <- do.call('c',lapply(list1[['2']], function(x) seq(as.Date(x)-2, as.Date(x), by="days")))
list_type3 <- do.call('c',lapply(list1[['3']], function(x) seq(as.Date(x)-2, as.Date(x), by="days")))

dd_2 <- data.frame (DD2 = list_type2, Type = as.character(rep('2', length(list_type2))), stringsAsFactors = F)
dd_3 <- data.frame (DD2 = list_type3, Type = as.character(rep('3', length(list_type3))), stringsAsFactors = F)
dummy_date = rbind(dd_2, dd_3)
seq_date <- seq(as.Date('2017-12-01'),as.Date('2018-01-31'), by = 'days')
all_dates <- data.frame (DD2 = rep(seq_date,2), Type = as.character(rep(c('2','3'),each = length(seq_date))),stringsAsFactors = F)

现在我们可以将您的数据框加入所有日期,这样一个月中的每一天都会得到一行

all_dates <- left_join(dd_date, dat_summary, by = c('DD2', 'Type')) 

我们可以用 dummy_date 过滤这个结果,它(我们记得)只包含数据最后一天之前所需的天数

df1<-  left_join(dummy_date, all_dates,  by = c('DD2', 'Type')) %>% arrange(Type, desc(DD2))

df1
       DD2 Type  med   sum
1  2018-01-20    2 14.0 25000
2  2018-01-19    2   NA    NA
3  2018-01-18    2   NA    NA
4  2017-12-30    2 19.0 30000
5  2017-12-29    2   NA    NA
6  2017-12-28    2   NA    NA
7  2018-01-27    3 23.0 50000
8  2018-01-26    3   NA    NA
9  2018-01-25    3 19.5 25000
10 2017-12-30    3 19.0 60000
11 2017-12-29    3   NA    NA
12 2017-12-28    3   NA    NA 

抱歉,'type' 没有正确设置为低和高,读取数据时出现问题。我希望这会有所帮助

编辑 添加了有关进入 DF2 的方法的建议

df1 %>% group_by(Type, month = floor_date(DD2, 'month')) %>% 
  summarise(sum = sum(sum, na.rm = T),
            n = max (n1, na.rm = T)) %>% 
  unite(sum.n, c('sum','n')) %>% 
  spread(Type, sum.n) %>%
  rename(low = '3', high = '2') %>%
  separate(high, c('high','n_high')) %>% 
  separate(low, c('low','n_low')) %>%
  mutate(dummy_low = as.integer(c(NA, low[1:length(low)-1])),
         dummy_high = as.integer(c(NA, high[1:length(high)-1])),
         low = as.integer(low), 
         high = as.integer(high))%>% 
    mutate(perc_low = 100*(low-dummy_low)/dummy_low)

# A tibble: 2 x 8
  month       high n_high   low n_low dummy_low dummy_high perc_low
  <date>     <int> <chr>  <int> <chr>     <int>      <int>    <dbl>
1 2017-12-01 30000 1      60000 1            NA         NA     NA  
2 2018-01-01 25000 1      75000 3         60000      30000     25.0

您可以为“高”和计数添加剩余的列。我确信该解决方案不是最优雅的解决方案,但它应该可以工作。 DF2 现在只有两个月,但这是因为您在示例中只提供了 2 个月。它应该适用于任意月份,然后您可以过滤过去三个月。

【讨论】:

  • 我已经通过这种方法在单独的数据框中进行了差异和计数,但我的主要问题是每天从DD2 按降序创建三行并保持逻辑,如果某些日期行不是可用而不是在DF1 中显示该日期,但值应为“0”,DF2 根据数据手册获取最近三个月的数据并计算% 的增加或减少。
  • 在这里感谢您的支持..:)
  • 我是否理解正确,您希望每月获取最后一天的数据和前两天的数据?因为如果您想要每个月的最后 3 天,有时您根本无法获得数据
  • 是....例如,如果我运行昨天的代码并且如果 27/01/2018 比我想要 27/01/2018、26/01/2018 和 25/01/2018 的数据自 2018 年 1 月以来的月份与 1 月 18 日、12 月 18 日和 11 月 18 日相同。如您所见,我在初始数据框中没有 26/01/2018 的行,但我也希望在 DF1 中有一行,但值为 0。
  • 对不起。为了澄清。 'take Median as per nrow' 是什么意思 = 你的意思是意思吗……(没有双关语;)
猜你喜欢
  • 1970-01-01
  • 2018-08-06
  • 2021-10-17
  • 2014-05-30
  • 2019-02-23
  • 1970-01-01
  • 2012-08-13
  • 2020-11-20
  • 1970-01-01
相关资源
最近更新 更多