OpenCPU和jsonlite:基于列的“/ json”与基于行的

时间:2014-03-28 16:56:24

标签: json r opencpu jsonlite

是否有一种干净的方法可以将data.frames上的默认“/ json”后缀选项更改为基于列而不是基于行?

如果我理解正确,R中的Data.frames实际上只是命名列表,其中每个列表的长度与其他列表的长度相同。使用jsonlite,可以很容易地显示差异(简单示例,是):

library(jsonlite)
ll <- list(xx=1:3, yy=6:8)
dd <- data.frame(xx=1:3, yy=6:8)
toJSON(dd)
# [1] "[ { \"xx\" : 1, \"yy\" : 6 }, { \"xx\" : 2, \"yy\" : 7 }, { \"xx\" : 3, \"yy\" : 8 } ]"
toJSON(ll)
# [1] "{ \"xx\" : [ 1, 2, 3 ], \"yy\" : [ 6, 7, 8 ] }"
toJSON(dd, dataframe='column')
# [1] "{ \"xx\" : [ 1, 2, 3 ], \"yy\" : [ 6, 7, 8 ] }"
toJSON(as.list(dd))
# [1] "{ \"xx\" : [ 1, 2, 3 ], \"yy\" : [ 6, 7, 8 ] }"

最后三个是相同的。通过将dataframe参数用于toJSON或将data.frame强制转换为list,可以很容易地强制它看起来相同。

使用OpenCPU的API,调用看起来很相似:

$ curl http://localhost:7177/ocpu/library/base/R/list/json -H "Content-Type: application/json" -d '{ "xx":[1,2,3], "yy":[6,7,8] }'
{
        "xx" : [
                1,
                2,
                3
        ],
        "yy" : [
                6,
                7,
                8
        ]
}

$ curl http://localhost:7177/ocpu/library/base/R/data.frame/json -H "Content-Type: application/json" -d '{ "xx":[1,2,3], "yy":[6,7,8] }'
[
        {
                "xx" : 1,
                "yy" : 6
        },
        {
                "xx" : 2,
                "yy" : 7
        },
        {
                "xx" : 3,
                "yy" : 8
        }
]

如果我希望data.frame本身是基于JSON的列,那么我需要将其强制转换为list

$ curl http://localhost:7177/ocpu/library/base/R/data.frame -H "Content-Type: application/json" -d '{ "xx":[1,2,3], "yy":[6,7,8] }'
/ocpu/tmp/x000a0fb8/R/.val
/ocpu/tmp/x000a0fb8/stdout
/ocpu/tmp/x000a0fb8/source
/ocpu/tmp/x000a0fb8/console
/ocpu/tmp/x000a0fb8/info

$ curl http://localhost:7177/ocpu/library/base/R/as.list/json -d "x=x000a0fb8"
{
        "xx" : [
                1,
                2,
                3
        ],
        "yy" : [
                6,
                7,
                8
        ]
}

三个问题:

  1. 有没有办法将OpenCPU auto-JSON -ification的默认行为更改为基于列?

  2. 是否有理由(除了“必须默认某事”)它默认为基于行? (这样我就能更好地理解基础和效率,而不是挑战。)

  3. 但这是学术性的,因为大多数(如果不是全部)接受JSON输出的库都会透明地理解和转换格式。右

  4. (Win7 x64,R 3.0.3,opencpu 1.2.3,jsonlite 0.9.4)

    (PS:谢谢,Jeroen,OpenCPU非常棒!我玩的越多,我就越喜欢。)

2 个答案:

答案 0 :(得分:3)

对于dataframe个对象,您可以使用HTTP GET并设置dataframe参数:

GET http://localhost:7177/ocpu/tmp/x000a0fb8/json?dataframe=rows

例如,Boston包中的MASS对象也是一个数据框:

https://cran.ocpu.io/MASS/data/Boston/json?dataframe=columns
https://cran.ocpu.io/MASS/data/Boston/json?dataframe=rows

对于HTTP GET端点的.../json个请求,所有http参数都映射到jsonlite package toJSON函数中的参数。您还可以指定其他toJSON参数:

https://cran.ocpu.io/MASS/data/Boston/json?dataframe=columns&digits=4

要查看哪些参数可用,请查看jsonlite manualthis post

请注意,这只适用于您执行两步操作的步骤:首先在返回HTTP POST的函数上dataframe,然后使用{json格式检索该对象{1}}请求。当您执行使用HTTP GET修复toJSON请求的一步快捷方式时,无法指定POST参数,因为在/json请求中,HTTP参数始终映射到函数调用。

此默认设置的原因是基于行的设计似乎是编码表格数据的最常规和可互操作的方式。 jsonlite paper/vignette详细介绍了一些问题。请注意,它也可以反过来:您不必调用POST函数来创建数据框,只需在表单中发布参数:

data.frame

会自动将其转换为数据框:

[{"xx":1,"yy":6},{"xx":2,"yy":7},{"xx":3,"yy":8}]

答案 1 :(得分:0)

如果您想避免GET请求,可以使用Javascript进行转换:

#include <stdio.h>

int main(void) {
    int max = 1000;
    for (int i = 0; i < max; ++i) {
        printf("%d", i);
        if (i % 4 == 3)
            putchar('\n');
    }
    return 0;
}

结果:

var df = [
        {"id":1,"Sepal.Length":5.1,"Sepal.Width":3.5,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"},
        {"id":2,"Sepal.Length":4.9,"Sepal.Width":3,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"},
        {"id":3,"Sepal.Length":4.7,"Sepal.Width":3.2,"Petal.Length":1.3,"Petal.Width":0.2,"Species":"setosa"}
        ]

var columns = Object.keys(df[0]);
var dfcolumns = {};
for (i = 0; i < columns.length; i++) {
    var column = [];
    var colname = columns[i];
    for (j = 0; j < df.length; j++) {
        column.push(df[j][colname]);
    }
    dfcolumns[colname] = column;
}

console.log(dfcolumns);