为什么这个条件查找不起作用?
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
d
、o
、w
、l
、e
position
的工人7
.
不幸的是,firm_type
的编码方式从 2005 年起发生了变化; 2005年之前,相关人员都在firm_type==1
的公司工作。此后,类型 1
和 2
是可接受的。
我试过这个查询,但它不起作用:
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)
很慢,因为它会计算所有 yes
和 no
,如果它需要它们中的任何一个,如 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,]
编辑:修改后反映了下面的评论
我有一些关于不同时间工人的数据。他们可以在任何给定的一年中担任多个职位;我想对数据进行子集化,以获得 至少一个 职位具有某些特征的工人。
这是我的玩具数据:
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
d
、o
、w
、l
、e
position
的工人7
.
不幸的是,firm_type
的编码方式从 2005 年起发生了变化; 2005年之前,相关人员都在firm_type==1
的公司工作。此后,类型 1
和 2
是可接受的。
我试过这个查询,但它不起作用:
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)
很慢,因为它会计算所有 yes
和 no
,如果它需要它们中的任何一个,如 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,]