Фильтр dplyr не работает должным образом в функции с теми же именами аргументов - PullRequest
0 голосов
/ 13 мая 2019

Я написал следующее простое 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


Что я делаюнеправильно?

Ответы [ 2 ]

2 голосов
/ 13 мая 2019

dplyr и друзья 1 умны, но не могут различить две ссылки на gearcarb). Например, в gear == as.numeric(gear) вы намереваетесь первым ссылаться на gear в кадре, а вторым ссылаться на аргумент функции, но в этих функциях первое совпадение gear (с точностью до кадра, внутри функциональная среда (в окружающих средах) выигрывает и используется для всех ссылок. В этом случае они оба соответствуют столбцу фрейма и поэтому всегда TRUE (в этом примере).

Попробуйте:

function(carb., gear.) {
  mtcars %>% filter(gear == as.numeric(gear.),
                    carb == as.numeric(carb.)) %>% 
    jsonlite::toJSON()

}

К сожалению, это приводит к тому, что аргументы API менее эстетичны. Поэтому, если вы хотите сохранить то, как они выглядят (или есть внешние мотиваторы, чтобы оставить их как есть), сделайте быстрое переназначение.

function(carb, gear) {
  c. <- carb
  g. <- gear
  mtcars %>%
    filter(gear == as.numeric(g.),
           carb == as.numeric(c.)) %>% 
    jsonlite::toJSON()
}

Примечание: иногда полезно внедрить разрешающую фильтрацию, где пропущенный (или намеренно нулевой) аргумент означает отсутствие фильтрации.

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},...]"

, который возвращает длинную строку (обратите внимание на кавычки). Многие парсеры увидят это как строку и сохранят ее. (Например, конвейер curl ... | jq . не открывает JSON должным образом, он просто возвращает буквальную строку.)

Вместо этого, если вы удалите 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},...]

, который является "правильным" возвращением json и может быть правильно проанализирован. Добавление | jq . после того, как вызов curl правильно анализирует вывод:

$ 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
  },
  ...
]

Примечания:

  1. Я должен отметить, что это не уникально для 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
    
0 голосов
/ 18 июля 2019

Просто заключите их в кавычки с помощью !!:

function(carb,gear) {
  mtcars %>% filter(gear == as.numeric(!! gear),
                    carb == as.numeric(!! carb)) %>% 
    jsonlite::toJSON()

}

См. Программирование с помощью dplyr .

...