dplyr 过滤器在具有相同参数名称的函数中无法按预期工作
dplyr filter not working as expected in function with same argument names
下面是我简单写的plumber api
library(plumber)
library(dplyr)
#* @apiTitle TESTS API
#* Returns json with filtered mtcars
#* @param carb
#* @param gear
#* @get /test2
#* @serializer unboxedJSON
function(carb,gear) {
mtcars %>% filter(gear == as.numeric(gear),
carb == as.numeric(carb)) %>%
jsonlite::toJSON()
}
唉,当我调用它时,mtcars 数据没有像我期望的那样被过滤:
library(httr)
library(jsonlite)
library(dplyr)
response <- GET(url = 'http://127.0.0.1:3098/test2',
query = list(gear = 4,
carb = 4),
encode="json")
content(response, encoding = "json") %>% fromJSON() %>% head()
mpg cyl disp hp drat wt qsec vs am gear carb
1 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
2 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
3 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
4 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
5 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
6 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
我做错了什么?
dplyr
和 friends1 很聪明,但他们无法区分对 gear
(和 carb
)的两个引用。例如,在 gear == as.numeric(gear)
中,您打算第一个引用框架内的 gear
,第二个引用函数参数,但在这些函数中,gear
的第一个匹配项(在框架内,在功能环境内,在封闭环境内)获胜并用于所有参考。在这种情况下,它们都匹配框架的列,因此总是 TRUE
(在本例中)。
尝试:
function(carb., gear.) {
mtcars %>% filter(gear == as.numeric(gear.),
carb == as.numeric(carb.)) %>%
jsonlite::toJSON()
}
不幸的是 side-effect API 的论点不够美观。因此,如果您想保留它们的外观(或者有外部激励因素来保持它们 as-is),请快速重新分配。
function(carb, gear) {
c. <- carb
g. <- gear
mtcars %>%
filter(gear == as.numeric(g.),
carb == as.numeric(c.)) %>%
jsonlite::toJSON()
}
旁注:我发现有时实现宽松过滤很有用,其中省略(或 intentionally-null)参数意味着不过滤。
function(carb = NA, gear = NA) {
c. <- carb
g. <- gear
mtcars %>%
filter(is.na(g.) | gear == as.numeric(g.),
is.na(c.) | carb == as.numeric(c.)) %>%
jsonlite::toJSON()
}
另一个旁注:你在这里做双重 JSON 是有原因的吗?例如,我看到:
$ curl -s localhost:8000/test2?gear=4
"[{\"mpg\":21,\"cyl\":6,\"disp\":160,\"hp\":110,\"drat\":3.9,\"wt\":2.62,\"qsec\":16.46,\"vs\":0,\"am\":1,\"gear\":4,\"carb\":4},...]"
这是一个长字符串 return(注意引号)。许多解析器会将其视为字符串并保留它。 (例如,管道 curl ... | jq .
没有 break-open 应该是 json,它只是 return 文字字符串。)
相反,如果您删除 toJSON
,您会看到:
$ curl -s localhost:8000/test2?gear=4
[{"mpg":21,"cyl":6,"disp":160,"hp":110,"drat":3.9,"wt":2.62,"qsec":16.46,"vs":0,"am":1,"gear":4,"carb":4},...]
这是一个"proper"jsonreturn,可以正确解析。在 curl 调用正确解析输出后添加 | jq .
:
$ curl -s localhost:8000/test2?gear=4 | jq .
[
{
"mpg": 21,
"cyl": 6,
"disp": 160,
"hp": 110,
"drat": 3.9,
"wt": 2.62,
"qsec": 16.46,
"vs": 0,
"am": 1,
"gear": 4,
"carb": 4
},
...
]
备注:
我要注意,这不是 dplyr
独有的,不应该责怪那里。使用 base::with
和 base::within
可以看到相同的行为。比较两者:
func <- function(carb, gear) { browser(); 1; }
func(1, 3)
# Called from: func(1, 3)
debug at #1: [1] 1
c. <- carb
g. <- gear
with(mtcars, { gear == as.numeric(gear) & carb == as.numeric(carb); })
# [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
# [16] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
# [31] TRUE TRUE
with(mtcars, { gear == as.numeric(g.) & carb == as.numeric(c.); })
# [1] FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
# [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
# [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
只需用 !!
取消对它们的引用:
function(carb,gear) {
mtcars %>% filter(gear == as.numeric(!! gear),
carb == as.numeric(!! carb)) %>%
jsonlite::toJSON()
}
下面是我简单写的plumber api
library(plumber)
library(dplyr)
#* @apiTitle TESTS API
#* Returns json with filtered mtcars
#* @param carb
#* @param gear
#* @get /test2
#* @serializer unboxedJSON
function(carb,gear) {
mtcars %>% filter(gear == as.numeric(gear),
carb == as.numeric(carb)) %>%
jsonlite::toJSON()
}
唉,当我调用它时,mtcars 数据没有像我期望的那样被过滤:
library(httr)
library(jsonlite)
library(dplyr)
response <- GET(url = 'http://127.0.0.1:3098/test2',
query = list(gear = 4,
carb = 4),
encode="json")
content(response, encoding = "json") %>% fromJSON() %>% head()
mpg cyl disp hp drat wt qsec vs am gear carb
1 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
2 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
3 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
4 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
5 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
6 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
我做错了什么?
dplyr
和 friends1 很聪明,但他们无法区分对 gear
(和 carb
)的两个引用。例如,在 gear == as.numeric(gear)
中,您打算第一个引用框架内的 gear
,第二个引用函数参数,但在这些函数中,gear
的第一个匹配项(在框架内,在功能环境内,在封闭环境内)获胜并用于所有参考。在这种情况下,它们都匹配框架的列,因此总是 TRUE
(在本例中)。
尝试:
function(carb., gear.) {
mtcars %>% filter(gear == as.numeric(gear.),
carb == as.numeric(carb.)) %>%
jsonlite::toJSON()
}
不幸的是 side-effect API 的论点不够美观。因此,如果您想保留它们的外观(或者有外部激励因素来保持它们 as-is),请快速重新分配。
function(carb, gear) {
c. <- carb
g. <- gear
mtcars %>%
filter(gear == as.numeric(g.),
carb == as.numeric(c.)) %>%
jsonlite::toJSON()
}
旁注:我发现有时实现宽松过滤很有用,其中省略(或 intentionally-null)参数意味着不过滤。
function(carb = NA, gear = NA) {
c. <- carb
g. <- gear
mtcars %>%
filter(is.na(g.) | gear == as.numeric(g.),
is.na(c.) | carb == as.numeric(c.)) %>%
jsonlite::toJSON()
}
另一个旁注:你在这里做双重 JSON 是有原因的吗?例如,我看到:
$ curl -s localhost:8000/test2?gear=4
"[{\"mpg\":21,\"cyl\":6,\"disp\":160,\"hp\":110,\"drat\":3.9,\"wt\":2.62,\"qsec\":16.46,\"vs\":0,\"am\":1,\"gear\":4,\"carb\":4},...]"
这是一个长字符串 return(注意引号)。许多解析器会将其视为字符串并保留它。 (例如,管道 curl ... | jq .
没有 break-open 应该是 json,它只是 return 文字字符串。)
相反,如果您删除 toJSON
,您会看到:
$ curl -s localhost:8000/test2?gear=4
[{"mpg":21,"cyl":6,"disp":160,"hp":110,"drat":3.9,"wt":2.62,"qsec":16.46,"vs":0,"am":1,"gear":4,"carb":4},...]
这是一个"proper"jsonreturn,可以正确解析。在 curl 调用正确解析输出后添加 | jq .
:
$ curl -s localhost:8000/test2?gear=4 | jq .
[
{
"mpg": 21,
"cyl": 6,
"disp": 160,
"hp": 110,
"drat": 3.9,
"wt": 2.62,
"qsec": 16.46,
"vs": 0,
"am": 1,
"gear": 4,
"carb": 4
},
...
]
备注:
我要注意,这不是
dplyr
独有的,不应该责怪那里。使用base::with
和base::within
可以看到相同的行为。比较两者:func <- function(carb, gear) { browser(); 1; } func(1, 3) # Called from: func(1, 3) debug at #1: [1] 1 c. <- carb g. <- gear with(mtcars, { gear == as.numeric(gear) & carb == as.numeric(carb); }) # [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE # [16] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE # [31] TRUE TRUE with(mtcars, { gear == as.numeric(g.) & carb == as.numeric(c.); }) # [1] FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE # [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE # [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
只需用 !!
取消对它们的引用:
function(carb,gear) {
mtcars %>% filter(gear == as.numeric(!! gear),
carb == as.numeric(!! carb)) %>%
jsonlite::toJSON()
}