【问题标题】:R - Duplicate rows while changing values in a particular fieldR - 在更改特定字段中的值时重复行
【发布时间】:2020-12-17 15:41:26
【问题描述】:

我有一个 dfName Date Count 作为字段。我想通过将日期增加 1 个月来复制每一行 Count 的次数。这是我的示例表:

Name Date Count
John 10-21-2019 12

我想要 John 的 12 行,日期递增 12 个月。样本结果:

Name Date Count
John 10-21-2019 12
John 11-21-2019 12
John 12-21-2019 12
John 1-21-2020 12
John 2-21-2020 12
John 3-21-2020 12
John 4-21-2020 12
John 5-21-2020 12
John 6-21-2020 12
John 7-21-2020 12
John 8-21-2020 12
John 9-21-2020 12

除了Date 列之外,我可能只想复制许多其他列。我如何在 R 中实现这一点?

【问题讨论】:

  • 用一个最小的可重现示例来改进帖子可能非常有用:)

标签: r dataframe


【解决方案1】:

这是一个 dplyr 方法:

library(dplyr)
dat %>%
  mutate(rn = row_number()) %>%
  rowwise() %>%
  slice(rep(1, Count)) %>%
  group_by(rn) %>%
  mutate(Date = seq(Date[1], by = "months", length.out = n())) %>%
  ungroup() %>%
  select(-rn)
# # A tibble: 12 x 3
#    Name  Date       Count
#    <chr> <date>     <int>
#  1 John  2019-10-21    12
#  2 John  2019-11-21    12
#  3 John  2019-12-21    12
#  4 John  2020-01-21    12
#  5 John  2020-02-21    12
#  6 John  2020-03-21    12
#  7 John  2020-04-21    12
#  8 John  2020-05-21    12
#  9 John  2020-06-21    12
# 10 John  2020-07-21    12
# 11 John  2020-08-21    12
# 12 John  2020-09-21    12

快速演练:

  • 添加 rn 以便我以后可以最轻松/自信地按它们分组(因为每个名称/日期组合可能出现任意次数,我在推断);
  • slice(rep(1, Count)) 选择第一行,Count 次,给出所需的重复行;因为这已经是逐行(rowwise()),这只是将行扩展Count
  • seq(Date[1], ...) 因为此时,Date 是一个可能大于 1 的向量,我们真的只关心重复日期中的第一个; by="months" and length.out=n()` 确保我们获得填充行数所需的间隔和跨度/周期
  • 使用 ungroup() 进行一些清理并删除 rn 帮助器列

更新

如果您需要将月份中的某天 (dom) 设为起始 dom 和该月份的天数中的较小者,请尝试此操作。

也就是说,使用"2019-01-31",按月排序不会跳转到"2019-02-31"(不存在),而是跳转到"2019-03-03"。如果你需要它跳转到"2019-02-28"(因为那是那个月的最后一天),那么试试这个功能。 (我们现在依赖lubridate 包。)

seq_similar_dom <- function(first, length.out) {
  dom <- lubridate::day(first)
  month1 <- first - dom + 1L
  months1 <- seq(month1, by = "months", length.out = length.out)
  months1 - 1L + pmin(dom, lubridate::days_in_month(months1))
}

让我们更新我们的示例并尝试几个极端情况。

首先,如果特定条目的dom 为 28 或更少,那么这将完全相同。如果是 29 或更高,虽然...

dat$Date <- as.Date("2014-07-31")
dat %>%
  mutate(rn = row_number()) %>%
  rowwise() %>%
  slice(rep(1, Count)) %>%
  group_by(rn) %>%
  mutate(Date = seq_similar_dom(Date[1], length.out = n())) %>%
  ungroup() %>%
  select(-rn)
# # A tibble: 12 x 3
#    Name  Date       Count
#    <chr> <date>     <int>
#  1 John  2014-07-31    12
#  2 John  2014-08-31    12
#  3 John  2014-09-30    12
#  4 John  2014-10-31    12
#  5 John  2014-11-30    12
#  6 John  2014-12-31    12
#  7 John  2015-01-31    12
#  8 John  2015-02-28    12
#  9 John  2015-03-31    12
# 10 John  2015-04-30    12
# 11 John  2015-05-31    12
# 12 John  2015-06-30    12

dat$Date <- as.Date("2019-01-31")
dat %>%
  mutate(rn = row_number()) %>%
  rowwise() %>%
  slice(rep(1, Count)) %>%
  group_by(rn) %>%
  mutate(Date = seq_similar_dom(Date[1], length.out = n())) %>%
  ungroup() %>%
  select(-rn)
# # A tibble: 12 x 3
#    Name  Date       Count
#    <chr> <date>     <int>
#  1 John  2019-01-31    12
#  2 John  2019-02-28    12
#  3 John  2019-03-31    12
#  4 John  2019-04-30    12
#  5 John  2019-05-31    12
#  6 John  2019-06-30    12
#  7 John  2019-07-31    12
#  8 John  2019-08-31    12
#  9 John  2019-09-30    12
# 10 John  2019-10-31    12
# 11 John  2019-11-30    12
# 12 John  2019-12-31    12

数据:

dat <- structure(list(Name = "John", Date = "10-21-2019", Count = 12L), class = "data.frame", row.names = c(NA, -1L))
dat$Date <- as.Date(dat$Date, format = "%m-%d-%Y")

【讨论】:

  • 我认为这行得通。但是当日期是一个月的 31 日时,它会重复两个月份。不知道你这边是否也一样。 2014-07-31、2014-08-31、2014-10-01、2014-10-31、2014-12-01、2014-12-31、2015-01-31、2015-03-03、2015- 03-31, 2015-05-01, 2015-05-31, 2015-07-01
  • 并非所有月份都有 31 天,因此您需要指定您的偏好。我无法推断。你想把它“四舍五入”到月底吗?这(对我来说)不是一个明显的假设,虽然它可以编程,但它并不像seq.Date 那样微不足道。你对2019-01-31 和一个月后有什么期望?
  • 对于不完整的要求,我深表歉意。更新工作得很好。非常感谢你的帮助。尽管考虑到我的数据大小它非常慢,并且还会引发警告,上面写着“警告消息:... 不是空的。我们检测到这些有问题的参数:* needs_dots
  • 我遇到了另一个问题。它通过发出警告成功运行:Grouping rowwise data frames strips rowwise 性质。因此,只有第一行被重复。其余的都没有发生。我在图书馆做错了吗?
  • 抱歉,我没有看到该警告。
【解决方案2】:

您可以利用嵌套数据框创建列表列,然后取消嵌套。

在这种情况下,这有点烦人,因为这似乎与 Date 字段有问题。所以我们需要切换到 Integer,然后再切换回 Date。

library(dplyr)
library(tidyr)
library(purrr)

dat %>% 
  mutate(Date = map2(Date, Count, ~ as.integer(seq(.x, by = "months", length.out = .y)))) %>% 
  unnest_longer(Date) %>% 
  mutate(Date = as.Date(Date, origin = "1970-01-01"))

这会给你扩展。

# A tibble: 12 x 3
   Name  Date       Count
   <chr> <date>     <int>
 1 John  2019-10-21    12
 2 John  2019-11-21    12
 3 John  2019-12-21    12
 4 John  2020-01-21    12
 5 John  2020-02-21    12
 6 John  2020-03-21    12
 7 John  2020-04-21    12
 8 John  2020-05-21    12
 9 John  2020-06-21    12
10 John  2020-07-21    12
11 John  2020-08-21    12
12 John  2020-09-21    12

【讨论】:

  • 日期列在我的代码中实际上称为“开始日期”,对于我正在调查的情况,计数始终为 12。所以我使用了以下内容:dat %&gt;% complete("Start Date" = seq("Start Date", by ="months", length.out = 12)) %&gt;% fill(Name, Count) 我收到一个错误,上面写着Error in seq.Date("Start Date", by = "months", length.out = 12) : 'from' must be of length 1
  • 很有趣,我原来的、更复杂的删除答案就是这样做的。我会把那个移到...
【解决方案3】:

base-R 方法可以是:

#Code
df1 <- df[rep(seq_len(nrow(df)), unique(df$Count)), ]
df1$Date <- seq(from=as.Date(min(df1$Date),'%m-%d-%Y'),by = '1 month',length.out = unique(df1$Count))
df1$Date <- format(df1$Date,'%m-%d-%Y')

输出:

df1
     Name       Date Count
1    John 10-21-2019    12
1.1  John 11-21-2019    12
1.2  John 12-21-2019    12
1.3  John 01-21-2020    12
1.4  John 02-21-2020    12
1.5  John 03-21-2020    12
1.6  John 04-21-2020    12
1.7  John 05-21-2020    12
1.8  John 06-21-2020    12
1.9  John 07-21-2020    12
1.10 John 08-21-2020    12
1.11 John 09-21-2020    12

使用的一些数据:

#Data
df <- structure(list(Name = "John", Date = "10-21-2019", Count = 12L), class = "data.frame", row.names = c(NA, 
-1L))

【讨论】:

  • 这很好,简洁的鸭子,但我认为如果原始数据框中有两行具有相同的计数,它会失败。您是否可能需要删除unique
  • @AllanCameron 你说得对 Dr Cameron,你认为unique(df$Count))[1] 可以缓解这个问题吗?
  • 如果你想在基础 R 中做所有事情,你可能需要split-lapply-bind
  • @AllanCameron 是的,你是对的,从技术上讲,行数应该是一个常数,这就是我使用这种方法的原因,但现在有 4 种解决方案,因此 OP 可以选择最好的:)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-15
  • 2018-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-26
  • 1970-01-01
相关资源
最近更新 更多