根据列值重塑 table

reshaping table based on column values

我正在研究根据值重塑 table 创建新列的问题。 我使用的示例与此处讨论的问题相同:

所以我有一个 table:

df:([]ID:1+til 5;
        Group:1 1 2 2 2;
        V1:10 + 2 * til 5;
        Type_v1:`t1`t2`t1`t1`t2;
        V2:3 0N 0N 7 8;
        Type_v2:`t2```t3`t3);

ID Group V1 Type_v1 V2 Type_v2
------------------------------
1  1     10 t1      3  t2     
2  1     12 t2                
3  2     14 t1                
4  2     16 t1      7  t3     
5  2     18 t2      8  t3     

目标是将其转换为按组和类型获取值的总和。请注意创建的新列。基本上 Type_v1 和 Type_v2 中的所有类型都用于为结果 table.

创建列
#   group v_1 type_1 v_2 type_2 v_3 type_3
#1:     1  10     t1  15     t2  NA   <NA>
#2:     2  30     t1  18     t2  15     t3

我做了开头,但我无法转换 table 并创建新列。 当然,我也在尝试以动态方式创建所有列,因为手动输入 20k 列是不可能的。

df1:select Group, Value:V1, Type:Type_v1 from df;
df2:select Group, Value:V2, Type:Type_v2 from df;
tr:df1,df2;
tr:0!select sum Value by Group, Type from tr where Type <> ` ;

基本上我错过了相当于:
dcast(tmp, group ~ rowid(group), value.var = c("v", "type"))

感谢任何帮助和解释,

您遗漏的最后一块是枢轴:https://code.kx.com/q/kb/pivoting-tables/

q)P:exec distinct Type from tr
q)exec P#(Type!Value) by Group:Group from tr
Group| t1 t2 t3
-----| --------
1    | 10 15
2    | 30 18 15

它不能完全为您提供准确的输出,但枢轴是概念

您可以扩展 Terry 的枢轴以使用函数形式动态执行上述 select 部分。在此处查看更多详细信息:

https://code.kx.com/q/basics/funsql/

// Personally, I would try to stay clear of column names too similar to reserved keywords in kdb
df: `id`grpCol`v_1`typCol_1`v_2`typCol_2 xcol df;

{[df;n]
   
    // dynamically create cols from 1 to n
    cls:`$("v_";"typCol_"),\:/:string 1 + til n;

    // functional form of select for each type/value col before joining together
    df:(,/) {?[x;();0b;`grpCol`v`typCol!`grpCol,y]}[df] each cls;

    // sum, then pivot
    df:0!select sum v by grpCol, typCol from df where typCol <> `;
    P:exec distinct typCol from df;
    df:exec P#(typCol!v) by grpCol:grpCol from df;

    // Type cols seem unnecessary but 
    // Can be done with another functional select
    ?[df;();0b;(`grpCol,raze P,'`$"typCol_",/:string 1 + til count P)!`grpCol,raze flip (P;enlist each P)]

    }[df;2]

grpCol  t1  typCol_1    t2  typCol_2    t3  typCol_3
1       10  t1          15  t2          0N  t3
2       30  t1          18  t2          15  t3

编辑 - 下面更详细的细分:

cls:`$("v_";"typCol_") ,\:/: string 1 + til n;

为列动态创建符号列表,因为在使用函数形式时列名需要它们。我首先创建一个 v_ 和 typCol_ 列表,最多为 n.

,\:/: -> 加入每个左迭代器和每个右迭代器

https://code.kx.com/q/ref/maps/#each-left-and-each-right

这允许我将左侧的每个项目(“v_”;“typCol_”)与右侧的每个项目连接起来。

同样可以用交叉实现,但你必须用翻转和剪切来重组列表 flip n cut `$("v_";"typCol_") cross string 1 + til n

(,/) {?[x;();0b;`grpCol`v`typCol!`grpCol,y]}[df] each cls; 

(,/) -> 这是与 join 一起使用的迭代器。它采用第一个 table,将其连接到第二个,然后将其连接到第三个,依此类推。

https://code.kx.com/q/ref/over/

{?[x;();0b;`grpCol`v`typCol!`grpCol,y]}[df] each cls

// functional select

?[table;  where;  by;  columns]
?[x;      ();     0b;  `grpCol`v`typCol!`grpCol,y]

这将创建一个 table 的列表,cls 变量中的每个列对 1。请注意,我没有像这样 {[x;y]} 那样在函数中明确声明 x 或 y。这是因为 x y 和 z 可以隐式使用,所以这个函数有或没有都有效。

这里的重要部分是最后一个参数(列)。对于函数式select,它是一个字典,其中列名作为键,列是什么作为值

e.g. `grpCol`v`typCol!`grpCol`v_1`typCol_1 -> 这是重命名每个 v 和 typCol,使它们相同,然后用 (,/) 将它们连接在一起。

有一个有用的关键字可以帮助计算函数形式 -> parse

parse"select Group, Value:V1, Type:Type_v1 from df"

0   ?
1   `df
2   ()
3   0b
4   (`Group`Value`Type)!`Group`V1`Type_v1
    P:exec distinct typCol from df;
    df:exec P#(typCol!v) by grpCol:grpCol from df;

此处概述了旋转:https://code.kx.com/q/kb/pivoting-tables/

它有效地 flips/rotates 了 table 的一段。它采用来自 typCol 的不同类型作为列,并使用 v 列作为每个对应的 typCol

的行
?[table;  where;  by;  columns]
?[df;();0b;(`grpCol,raze P,'`$"typCol_",/:string 1 + til count P)!`grpCol,raze flip (P;enlist each P)]

再次查看函数 select 中的最后一个参数,即列。这是动态生成后的样子:

(`grpCol`t1`typCol_1`t2`typCol_2`t3`typCol_3)!(`grpCol;`t1;enlist `t1;`t2;enlist `t2;`t3;enlist `t3)

这是一种获取类型列的 hacky 方法,我 select 每个 t1 t2 t3 都有一个 typeCol_1 _2 _3,

`t1 = (column) `t1
`typCol_1 = enlist `t1 -> the enlist here tells kdb I want the value `t1 rather than the column