【问题标题】:Populate a large data frame with calculated values使用计算值填充大型数据框
【发布时间】:2018-08-18 03:16:21
【问题描述】:

我有一个相当大的数据框(1.65 MM 行),如下所示:

  date        dayofyear time humidity temp
1 2008-01-01         1  300       99  38.2
2 2008-01-01         1  302       99  38.5
3 2008-01-01         1  304       99  38.5
4 2008-01-01         1  306       99  38.6
5 2008-01-01         1  308       99  38.9
6 2008-01-01         1  310       99  39.1
....

我想计算每一行的太阳正午并将其作为新列添加到数据框中。 suncalc::getSunlightTimes(date) 函数为我提供任何给定日期的太阳正午。

如果我这样做:

solarNoons <- suncalc::getSunlightTimes(date = as.Date(dataFrame$date))

然后(显然)即使在一小时后,命令也没有完成。

一个观察结果——即使数据框中有 1.65 MM 行,也只有大约 3000 个唯一日期。解决这个问题的最佳方法是什么?一次调用getSunlightTimes() 是否可以仅调用 3000 次,并且仍然使用新列中的值填充数据帧的 1.65 MM 行中的每一行?

这是我第一次发帖,但长时间阅读和学习。非常感谢。

【问题讨论】:

  • 使用unique 函数将这些唯一日期隔离到它们自己的data.frame。计算这个最小集合的太阳正午。将结果合并回原始数据。如果没有可重复的示例,我只能提供此建议来帮助您继续前进(以及进一步向 Google 提供一些“流行语”)
  • 您是否还需要传递latlon 坐标才能获得“solarNoon”时间?此外,使用keep 参数只计算“solarNoon”,避免其他不需要的计算。
  • 尽早分配df$date &lt;- as.Date(df$date),最好在你读完之后立即分配。字符串比日期对象更笨拙。这样您就可以避免创建一个包含 1.65m 字符串的临时字符串,其中 99% 以上都是重复的。
  • 你完全不需要这个,但是R supports caching function calls,所以再次调用它 1.65m 上只有 3000 个唯一值只会导致 3000 次调用。但只需使用 dplyr::group_by(date)
  • 这都是很好的反馈。我已经实现了。非常感谢。

标签: r dataframe


【解决方案1】:

以下应该有效。假设我们生成一个 200 万行的数据框:

> N <- 2e6
> R <- data.frame(year = sample(2000:2009,N,TRUE),
+                 dayofyear = sample(365,N,TRUE),
+                 time = floor(runif(N,0,12))*100+floor(runif(N,0,60)),
+                 humidity = 99,
+                 temp = floor(runif(N,15,40)))
> R$date <- as.Date(with(R,strptime(paste(year,dayofyear),
+                                   "%Y %j", tz="GMT")))
> nrow(R)
[1] 2000000
> head(R)
  year dayofyear time humidity temp       date
1 2000       206  307       99   39 2000-07-24
2 2009       101 1019       99   16 2009-04-11
3 2004       307  547       99   21 2004-11-02
4 2003       270 1158       99   33 2003-09-27
5 2006        21  330       99   22 2006-01-21
6 2005       154  516       99   21 2005-06-03
> 

在这种情况下,date 已经是 Date 列,但如果你的列是字符列,那么:

> R$date <- as.Date(R$date)

应该只需要几秒钟。

现在,获取所有唯一日期值的列表。这应该很快:

> dates <- unique(R$date)
> print(length(dates))
[1] 3650
> 

现在,在这个向量上运行getSunlightTimes。在我的机器上使用 suncalc 0.4 版和 R 3.4.4 版只花了几秒钟:

> times <- suncalc::getSunlightTimes(dates, lat=0, lon=0)

现在,生成一个索引向量,给出在唯一日期向量datesR$date 中每个日期的索引:

> i <- match(R$date, dates)

现在,通过同一索引选择 times 数据框的行:

> solarNoons <- times[i,]
> nrow(solarNoons)
[1] 2000000
> 

如果我们选择一行 R:

> R[1234567,]
        year dayofyear time humidity temp       date
1234567 2002        24  535       99   17 2002-01-24

您会看到solarNoons 的对应行是该日期的结果:

> solarNoons[1234567,]
                        date lat lon           solarNoon               nadir
2616.352 2002-01-24 12:00:00   0   0 2002-01-24 12:13:14 2002-01-24 00:13:14
                     sunrise              sunset          sunriseEnd
2616.352 2002-01-24 06:09:42 2002-01-24 18:16:46 2002-01-24 06:11:58
                 sunsetStart                dawn                dusk
2616.352 2002-01-24 18:14:30 2002-01-24 05:47:49 2002-01-24 18:38:39
                nauticalDawn        nauticalDusk            nightEnd
2616.352 2002-01-24 05:22:22 2002-01-24 19:04:06 2002-01-24 04:56:50
                       night       goldenHourEnd          goldenHour
2616.352 2002-01-24 19:29:38 2002-01-24 06:38:39 2002-01-24 17:47:49
> 

如果需要,可以将两个数据框合并在一起:

> R2 <- cbind(R, solarNoons)

这一切都假设“1.65 MM”意味着 165 万。如果您的意思是 165 万(即一万亿美元),那么您将需要一台更大的计算机。

【讨论】:

  • 感谢您抽出宝贵时间! uniquematch[i, ]cbind 带我回家!
【解决方案2】:

仅对 df$date 的每个不同值调用一次昂贵的函数。 现在您将只调用它 3000 次而不是 1.65m 次。应该快 550 倍。同时应用@RonakShah 建议的任何特定于功能的加速技巧。

# Assign df$date <- as.Date(df$date) as early as possible, ideally right after you read it in    
df$date <- as.Date(df$date)

library(dplyr)

df <- df %>% group_by(date) %>%
             mutate(solarNoon = suncalc::getSunlightTimes(df$date, ...))

有两种方法可以确保您在每个日期只调用一次suncalc::getSunlightTimes()

  1. group_by(date) %&gt;% ... 输入您的 suncalc::getSunlightTimes(df$date, ...) 电话
  2. use caching

我更喜欢方法 1。因为它很容易编码,并且总体上可以教你很好的分解。始终尝试构建代码以避免不必要地调用昂贵的函数一百万次。

【讨论】:

  • 在极不可能发生的情况下,这仍然太慢/挂起,以 10000 行或其他块的形式迭代您的 df。
  • 由于某种原因,这在我的机器上不起作用。命令不会完成。我也相信这应该有效。
  • @C.Mitty:您是否遵循我的建议“在极不可能发生的情况下,这仍然太慢/挂起,以 10000 行或其他块的形式迭代您的 df。”试试df[1:10000,] %&gt;% group_by(date) ...
猜你喜欢
  • 2015-04-01
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多