【问题标题】:filter by date range in sqldf按 sqldf 中的日期范围过滤
【发布时间】:2017-12-21 21:45:52
【问题描述】:

我正在尝试使用 sqldf 根据日期范围过滤数据框,如下面的示例代码。我有像下面的示例数据这样的数据。 sqldf 返回的 datedf 数据帧没有记录。该日期范围内的 SHV 数据框中有记录,任何人都可以看到我做错了什么,并让我知道如何在 sqldf 中按日期范围进行过滤。对我来说,日期在 r 中总是很棘手。

Code:
datedf<-sqldf("select field1                            
            ,fieldDate
                            from SHV
                            where fieldDate between '2004-01-01' and '2005-01-01'
                            ")


Data:

dput(SHV[1:50,c("field1","fieldDate")])
structure(list(field1 = c(1378L, 1653L, 1882L, 2400L, 
2305L, 2051L, 2051L, 2051L, 1796L, 2054L, 2568L, 1290L, 1804L, 
1804L, 3855L, 1297L, 2321L, 2321L, 2321L, 2071L, 2071L, 2074L, 
2588L, 1567L, 1317L, 1317L, 808L, 808L, 1321L, 2350L, 1586L, 
2613L, 1590L, 2614L, 2107L, 1340L, 1085L, 1085L, 2365L, 1344L, 
1601L, 1858L, 1603L, 1603L, 1860L, 2376L, 1355L, 1867L, 2382L, 
1872L), fieldDate = structure(c(12551, NA, NA, 14057, 15337, 
12919, 13336, 10325, 14984, 15643, 12864, 11242, 10749, 11207, 
10602, NA, 12646, 15649, NA, NA, NA, NA, NA, 17015, 13938, NA, 
16693, NA, NA, 12634, 12614, 10689, 12755, 10844, 11375, 4899, 
17298, 10905, 11450, NA, 10330, 15429, 12634, 10504, 12625, 11081, 
10939, NA, 12934, 11176), class = "Date")), .Names = c("field1", 
"fieldDate"), row.names = c(NA, 50L), class = "data.frame")

【问题讨论】:

  • 没有该日期范围的记录。
  • SQLite 没有日期或时间数据类型,因此它将 Date 类变量作为 R Date 类型的内部表示形式发送,即自纪元以来的天数。如果您使用 RH2 后端(H2 确实有日期类型)而不是默认的 RSQLite 后端,那么您的 SQL 语句将按原样工作。 library(sqldf); library(RH2); sqldf(...your statement...)

标签: r sqldf


【解决方案1】:

在此数据示例中,您在该日期范围内没有记录:

SHV[SHV$fieldDate >= "2010-01-01" & SHV$fieldDate < "2011-01-01",]
  field1 fieldDate
NA        NA      <NA>
NA.1      NA      <NA>
NA.2      NA      <NA>
NA.3      NA      <NA>
NA.4      NA      <NA>
NA.5      NA      <NA>
NA.6      NA      <NA>
NA.7      NA      <NA>
NA.8      NA      <NA>
NA.9      NA      <NA>
NA.10     NA      <NA>
NA.11     NA      <NA>
NA.12     NA      <NA>

【讨论】:

  • 同意 - 2008-06-27 和 2011-01-01 之间存在差距。但是语法有问题。将范围从 2000 年扩展到 2011 年仍然返回零行。
  • @wibeasley 谢谢,是的,我没有检查样本数据以确保日期范围内有记录。我已经更新了原始帖子,因此示例日期的日期范围内现在有记录。你知道我的代码有什么问题吗?
【解决方案2】:

根据sqldf()documentation,需要将日期格式化为它们的数值,以便将它们作为日期处理。这可以在生成 SQL 查询时使用sprintf() 完成。

SHV <- structure(list(field1 = c(1378L, 1653L, 1882L, 2400L, 
                          2305L, 2051L, 2051L, 2051L, 1796L, 2054L, 2568L, 1290L, 1804L, 
                          1804L, 3855L, 1297L, 2321L, 2321L, 2321L, 2071L, 2071L, 2074L, 
                          2588L, 1567L, 1317L, 1317L, 808L, 808L, 1321L, 2350L, 1586L, 
                          2613L, 1590L, 2614L, 2107L, 1340L, 1085L, 1085L, 2365L, 1344L, 
                          1601L, 1858L, 1603L, 1603L, 1860L, 2376L, 1355L, 1867L, 2382L, 
                          1872L), fieldDate = structure(c(12551, NA, NA, 14057, 15337, 
                                                              12919, 13336, 10325, 14984, 15643, 12864, 11242, 10749, 11207, 
                                                              10602, NA, 12646, 15649, NA, NA, NA, NA, NA, 17015, 13938, NA, 
                                                          16693, NA, NA, 12634, 12614, 10689, 12755, 10844, 11375, 4899, 
                                                          17298, 10905, 11450, NA, 10330, 15429, 12634, 10504, 12625, 11081, 
                                                          10939, NA, 12934, 11176), class = "Date")), .Names = c("field1", 
                                                                                                                 "fieldDate"), row.names = c(NA, 50L), class = "data.frame")

library(sqldf)
sqlStmt <- paste("select field1, fieldDate from SHV",
                 "where fieldDate between ",
                 sprintf("%d and %d",as.Date('2004-01-01','%Y-%m-%d'),
                     as.Date('2005-01-01','%Y-%m-%d')))
datedf<-sqldf(sqlStmt)
datedf

> datedf
  field1  fieldDate
1   1378 2004-05-13
2   2321 2004-08-16
3   2350 2004-08-04
4   1586 2004-07-15
5   1590 2004-12-03
6   1603 2004-08-04
7   1860 2004-07-26
> 

sprintf() 语句将日期转换为数值,从而确保 SQL 中的 between 运算符正常工作。

> sqlStmt
[1] "select field1, fieldDate from SHV where fieldDate between  12418 and 12784"
>

【讨论】:

  • 这可以简化为:fromDate &lt;- as.Date('2004-01-01'); toDate &lt;- as.Date('2005-01-01'); fn$sqldf("select field1, fieldDate from SHV where fieldDate between $fromDate and $toDate")
【解决方案3】:

根据this article,在执行sqldf之前,应该将日期字段转换为字符。

在将任何日期传递给 SQLdf 之前,我们需要先将它们转换为字符串。否则,SQLdf 将尝试将它们视为数字——这会导致很多心痛。

...

相反,我们应该将 DateCreated 列转换为字符串而不是日期。然后,SQL 会真正将它从字符串转换为日期。

困惑?想象一下当我试图自己解决这个问题时的我。

所以你的代码可能是:

SHV$fieldDate <- as.character(SHV$fieldDate)

datedf <- sqldf("
  SELECT
    field1,
    fieldDate
  FROM SHV
  WHERE fieldDate between '2004-01-01' and '2005-01-01'
  --WHERE '2004-01-01' <= fieldDate --and fieldDate <= '2005-01-01'
  ORDER BY fieldDate
")

# Both should equal 7.  Verify that null rows are handled as desired.
nrow(datedf)
sum(as.Date('2004-01-01') <= SHV$fieldDate & SHV$fieldDate <= as.Date('2005-01-01'), na.rm=T)

我希望它对何时将看起来日期的变量转换为实际日期有更多解释。如果您环顾四周,@g-grothendieck 的 SO response 采用了不同的方法,并将 sqldf 查询中的数据类型等同起来。

【讨论】:

  • 将数字日期值转换为字符是有风险的,因为它不适用于所有日期格式,例如 '%d-%m-%Y' 其中字符比较 '12-01-2004 ' > '01-02-2004' 返回 TRUE,即使 2004 年 2 月 1 日大于 2004 年 1 月 12 日。
  • 据我所知,as.character(SHV$fieldDate) 将始终返回ISO 8601。在这段代码中不需要像 '%d-%m-%Y' 这样的东西,因为它是以 Date 类型开始的。
  • 我同意在这个具体的例子中不需要像'%d-%m-%Y'这样的东西,它是为了说明两个日期之间的字符比较返回错误的情况回答。我已经看到编码人员在多种编程语言中进行此类比较会产生缺陷。
猜你喜欢
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-25
  • 2018-06-19
  • 1970-01-01
相关资源
最近更新 更多