为什么这个条件查找不起作用?

Why doesn't this conditional lookup work?

编辑:修改后反映了下面的评论

我有一些关于不同时间工人的数据。他们可以在任何给定的一年中担任多个职位;我想对数据进行子集化,以获得 至少一个 职位具有某些特征的工人。

这是我的玩具数据:

set.seed(1643)
dt<-data.table(id=rep(1:1000,10),
               area=sample(letters,1e4,replace=T),
               position=sample(10,1e4,replace=T),
               firm_type=sample(5,1e4,replace=T),
               year=rep(2001:2010,each=1000),key="id")

我只想要 area dowle position 的工人7.

不幸的是,firm_type 的编码方式从 2005 年起发生了变化; 2005年之前,相关人员都在firm_type==1的公司工作。此后,类型 12 是可接受的。

我试过这个查询,但它不起作用:

dt[.(dt[firm_type %in% ifelse(year<2005,1,1:2)
    &area %in% c("d","o","w","l","e")
    &position==7,unique(id)])]

具体来说,%in% 运算符,如以下评论中所述,不能逐行运算,因此我们得到(中间)输出,如:

> dt[firm_type %in% ifelse(year<2005,1,1:2)
+    &area %in% c("d","o","w","l","e")
+    &position==7,table(firm_type,year)]
         year
firm_type 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
        1    4    2    5    2    3    7    1    0    4    1
        2    2    4    4    6    4    5    9    8    1    2

@Frank 启发了这个解决方法:

dt[.(dt[ifelse(year<2005,firm_type==1,
           firm_type %in% 1:2)
    &area %in% c("d","o","w","l","e")
    &position==7,unique(id)])]

我对此很满意,但我很高兴看到是否有更好的方法来解决这个问题,因为 ifelse 没有优化。

更快的方法。您可以推迟 ifelse 直到您有一个更小的子集:

dt[ position==7L & area%in%c("d","o","w","l","e") & firm_type%in%1:2
][ifelse(year<2005,firm_type==1L,firm_type %in% 1:2),
  unique(id)
]

根据您觉得它的可读性,您还可以这样做:

dt[ position==7L & area%in%c("d","o","w","l","e") & firm_type%in%1:2
][!(year < 2005 & firm_type==2L),
  unique(id)
]

关于 ifelse. ifelse(cond,yes,no) 很慢,因为它会计算所有 yesno,如果它需要它们中的任何一个,如 documented by @RicardoSaporta。另一个想法——(cond&yes)|((!cond)&no)——在 OP 的早期迭代中提到,也有同样的问题。


冗长的方式。如果您的条件比较混乱,您可能希望将它们明确化:

my_areas     = c("d","o","w","l","e")
my_posns     = 7L
my_yearfirms = data.table(year=unique(dt$year))[,.(
  firm_type = if (year<2005) 1L else 1:2
),by=year]

merge(dt[position%in%my_posns & area%in%my_areas],my_yearfirms,by=c("year","firm_type"))[,
  unique(id)
]

最后一段代码可以

  • 跳过(根据上下文猜测发生了什么)和
  • 在别处重复使用(如果您更改条件)。

除非效率非常重要,否则我会这样做。

除 "and" 外,只需使用 "or":

> dt[((firm_type == 1 ) | (firm_type ==2 & year>=2005))
+    &area %in% c("d","o","w","l","e")
+    &position==7,]