【问题标题】:How to subset multiple data frame efficiently in 'R'?如何在“R”中有效地对多个数据框进行子集化?
【发布时间】:2015-07-16 03:03:20
【问题描述】:

我有一个大气 PM10 数据的大型“NetCDF”文件。您可以从here下载。我正在解释我的问题的详细信息。

这个 ncdf 文件有 8 个像这样的变量。

[1] "file ~/Downloads/2012_03_05_PM10_surface.nc has 8 dimensions:"
[1] "data_num   Size: 683016"
[1] "ncl1   Size: 683016"
[1] "obsnum_urban   Size: 250"
[1] "ID_LAT_LON   Size: 3"
[1] "obsnum_road   Size: 33"
[1] "obsnum_background   Size: 5"
[1] "obsnum_rural   Size: 16"
[1] "ncl7   Size: 683016"
[1] "------------------------"
[1] "file ~/Downloads/2012_03_05_PM10_surface.nc has 8 variables:"
[1] "int TMSID[data_num]  Longname:TMSID Missval:NA"
[1] "int TIME[ncl1]  Longname:TIME Missval:NA"
[1] "float PM10[data_num]  Longname:PM10 Missval:1e+30"
[1] "float urban[ID_LAT_LON,obsnum_urban]  Longname:urban Missval:1e+30"
[1] "float road[ID_LAT_LON,obsnum_road]  Longname:road Missval:1e+30"
[1] "float background[ID_LAT_LON,obsnum_background]  Longname:background Missval:1e+30"
[1] "float rural[ID_LAT_LON,obsnum_rural]  Longname:rural Missval:1e+30"
[1] "int TMS_JULIAN[ncl7]  Longname:TMS_JULIAN Missval:NA"

在这里,我的兴趣只有 4 个变量。它们是:

TIMSID 是站点的数量(包括城市站点、农村站点、道路、背景等)

urban :: 城市站点数 [urban 是 3 行 250 列矩阵。第1行是城市站点数,第2行是纬度,第3行是经度。]

TIME :: 数据收集时间为 2012 年 3 月 1 日凌晨 1 点至 2012 年 5 月 [“时间”编码为 YYYYMMDDHH]

PM10 :: 在每个站点的每个站点测量的每小时颗粒物浓度

从这个 ncdf 文件中,我已经对 2012 年 3 月 1 日凌晨 1 点(2012030101)的城市站点的 PM10 值进行了子集化。在这里,如您所知,TMSID 是所有站点的 id,但我只想为城市站点(而不是农村、道路等)进行子集化,所以我只匹配了 2012 年 3 月 1 日凌晨 1 点来自 TMSID 的城市 id。这意味着我有仅对城市站点 3 月 1 日的 1 小时 PM10 数据进行了子集化。我使用了以下代码:

library(ncdf)
nc<-open.ncdf("2012_03_05_PM10_surface.nc")
print(nc)

urban<-get.var.ncdf(nc,"urban")
time<-get.var.ncdf(nc,"TIME")
pm10<-get.var.ncdf(nc,"PM10")
tmsid<-get.var.ncdf(nc,"TMSID")
urban<-as.data.frame(t(urban))
colnames(urban)<-c("ID","LAT","LON")

urban311<-lapply(urban$ID,
                 function(x)data.frame(ID=x,time=2012030101,
                                       PM10=pm10[tmsid%in%x &
                                                   time%in%2012030101]))
urban311<-do.call(rbind,urban311)
urban311<-merge(urban311,urban,by="ID")
urban311
urban311<-subset(urban311,select=c("time","ID","LAT","LON","PM10"))

seoul311<-subset(urban311, LAT>=36.8 & LAT <=38 & LON>=126.4 & LON<= 127.3)
rownames(seoul311)<-NULL

在上述代码的最后 2 行中,我根据纬度和经度仅从城市站点的特定区域获得了 PM10 值的子集。最后我得到了一个这样的数据框。

              time     ID      LAT      LON PM10
    1   2012030101 111121 37.56464 126.9760   42
    2   2012030101 111123 37.57203 127.0050   37
    .
    .
    .
   106  2012030101 831153 37.49195 126.7533   68
   107  2012030101 831154 37.52662 126.8064   57

如您所知,这是一个仅适用于 3 月 1 日凌晨 1 点的数据框。现在我想从 3 月 1 日到 3 月 7 日的每个小时都做同样的工作。这意味着我想获得 (7*24) 数据框。我怎样才能有效地做到这一点?

如果您还有其他问题,请问我。提前致谢。

【问题讨论】:

  • 所以您唯一需要做的就是从urban311 行开始,只需将time%in%2012030101 更改为time%in%2012030102time%in%2012030103 等?将这些行包装在一个函数中,让它返回数据集,然后使用lapply 获取每小时的数据帧列表。那行得通吗?
  • @rawr,我是 R 语言的初学者。所以我可能无法理解你的想法。但我可以通过粘贴相同的代码 (7*24) 次来完成这项工作,只需替换 20120302、20120303 等时间。但这变得如此冗长和笨拙。
  • 我强烈,强烈推荐使用raster 包。太奇妙了。在这种情况下,它会使事情变得简单得多。它的文档非常好。另外,请考虑使用ncdf4 而不是ncdf

标签: r subset lapply netcdf


【解决方案1】:

这里不需要使用lapply。 此外,与其获取 7*24 数据帧,不如将 一个 数据帧包含所有日期,然后您可以根据需要对其进行子集化。

这一切都发生了,而不是你的 urban311 东西。 首先列出我们想要保留的所有times:

dts.to.get <- seq(as.POSIXct('2012-03-01 01:00'), as.POSIXct('2012-03-07 00:00'), by='1 hour')
# convert to the 2012030101 numeric format you have
dts.number <- as.numeric(format(dts.to.get, '%Y%m%d%H'))

然后找出哪些索引是城市 ID 并且有正确的时间:

i <- tmsid %in% urban$ID & time %in% dts.number
x <- data.frame(ID=as.vector(tmsid[i]), time=as.vector(time[i]), PM10=as.vector(pm10[i]))

请注意,subset(x, time==2012030101) 是您的 urban311x 包含您所追求的所有不同日期时间。

然后,如果您想添加 LATLON,请像以前一样使用 merge。请注意,由于每个 ID 出现 7*64 次,因此在您的数据框中复制了 168 次,因此最好将它们分开。

x <- merge(x, urban, by='ID')

没有必要做额外的subset(urban311, select=c("time", "ID", "LAT", "LON", "PM10")),因为它们是urban311 唯一的列。

如果你真的真的想将x 拆分为每个日期小时的一个数据帧,那么你可以这样做

lapply(unique(x$time), function (tt) subset(df, time == tt))

获取数据帧列表,但实际上,这是不值得的。需要很长时间,并且根据需要更快地发送至subset

【讨论】:

  • 我的理解是如果我想制作 (7*24) 不同的数据集/数据框,那么我必须通过替换时间每小时复制粘贴 subset(x, time==2012030101) (7*24) 次.我对吗?不是很长吗?我不知道,是否有可能在短时间内制作(7*24)不同的数据集。
  • 另一个问题是,你写了一个像x &lt;- data.frame(ID=as.vector(tmsid[i]), time=as.vector(time[i]), PM10=as.vector(pm10[i])) 这样的代码,但即使我这样写x&lt;-data.frame(ID=tmsid[i], Time=time[i], PM10=pm10[i]),我得到的输出也是一样的。为什么会这样?
  • 第一个问题:我的lapply 行为您完成了这一切,列表中的每个元素都是您的数据框之一。您不想单独命名它们,否则您必须按照您提到的那样输入每一行;因此,我将它们保存在一个列表中。第二个问题:使用as.vector 与不使用没有真正的区别,我只是注意到,如果你不这样做,class(x$ID) 等将是“数组”,而不是像大多数数据帧那样的数字向量。我不知道为什么会这样,老实说,我认为这不会有所作为。
  • 你的意思是如果我想要(7*24)个单独的数据框对象,我必须写subset(x, time==2012030101)这个代码(7*24)次?或者有什么方法可以通过1或2行制作(7 * 24)数据框对象?对不起,我是 r 的初学者。因为我必须为每小时数据绘制半变异函数。如果我有一个 (7*24) 数据框列表,那么我如何使用该列表中的一个数据框?
  • 不,你不必写subset 168 次,我已经提到如何使用lapply 一次完成所有操作。如果您想使用该列表中的一个数据框,请使用列表子集来获取该数据框,或者使用循环/lapply 对每个数据框进行操作。
【解决方案2】:
library(ncdf)
nc<-open.ncdf("2012_03_05_PM10_surface.nc")
print(nc)

urban<-get.var.ncdf(nc,"urban")
time<-get.var.ncdf(nc,"TIME")
pm10<-get.var.ncdf(nc,"PM10")
tmsid<-get.var.ncdf(nc,"TMSID")

urban<-as.data.frame(t(urban))
colnames(urban)<- c("ID","LAT","LON")

dates<-seq(as.POSIXct("2012-03-01:01:00"),
           as.POSIXct("2012-03-08:00:00"), by="1 hour")
dates.numeric <-as.numeric(format(dates, "%Y%m%d%H"))

i<-tmsid %in% urban$ID & time %in% dates.numeric
urban1to7<-data.frame(ID=as.vector(tmsid[i]), 
              time= as.vector(time[i]),
              PM10=as.vector(pm10[i]))
urban1to7<-merge(urban1to7,urban,by="ID")
urban311<-subset(urban1to7, time=2012030101)

#urban sites,seoul area,7 days,every hour
seoul1to7<-subset(urban1to7,LAT>=36.8 & LAT<=38 & LON>=126.4 & LON<=127.3)

# make a list where there is (7*24) data frames
lapply(unique(seoul1to7$time), function(x) subset(seoul1to7, time==x))

通过这种方式,我们可以通过 lapply 创建一个包含 (7*24) 个数据帧的列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 2012-01-23
    • 2021-12-04
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 2014-11-13
    相关资源
    最近更新 更多