DataFramesMeta.jl, 面向DataFrames的元编程工具

分享于 

13分钟阅读

GitHub

  繁體 雙語
Metaprogramming tools for DataFrames
  • 源代码名称:DataFramesMeta.jl
  • 源代码网址:http://www.github.com/JuliaStats/DataFramesMeta.jl
  • DataFramesMeta.jl源代码文档
  • DataFramesMeta.jl源代码下载
  • Git URL:
    git://www.github.com/JuliaStats/DataFramesMeta.jl.git
    Git Clone代码到本地:
    git clone http://www.github.com/JuliaStats/DataFramesMeta.jl
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/JuliaStats/DataFramesMeta.jl
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    DataFramesMeta.jl

    DataFramesMetaCoverallsTravisAppVeyor

    面向DataFrames和关联对象的元编程工具。 这些 MACROS 提高性能并提供更方便的语法。

    特性

    @with

    @with 允许将DataFrame列作为表达式中 :colX的符号引用。 如果在 ^(expr) 中封装了表达式,expr 将通过未被。 在 _I_(expr) 中包装表达式时,变量 expr 会引用该列,而不是使用符号。 以下是一些示例:

    using DataArrays, DataFramesusing DataFramesMeta
    df =DataFrame(x =1:3, y = [2, 1, 2])
    x = [2, 1, 0]@with(df, :y+1)@with(df, :x+ x) # the two x's are differentx =@with df begin res =0.0for i in1:length(:x)
     res +=:x[i] *:y[i]
     end resend@with(df, df[:x.>1, ^(:y)]) # The ^ means leave the :y alonecolref =:x@with(df, :y+_I_(colref)) # Equivalent to df[:y] + df[colref]

    这也适用于关联类型:

    y =3d =Dict(:s=>3, :y=>44, :d=>5)@with(d, :s+:y+ y)

    @with 是其他元编程工具使用的基本宏。

    如果 @with 是一个函数,那么在 @with 中的作用域是一个硬作用域( 如-blocks或者其他函数定义)。 可以读取父对象中的变量。 根据父作用域的类型,在父作用域中写入变量的方式有所不同。 如果父作用域是全局作用域,则不使用 global 关键字就不能分配变量。 如果父作用域是本地范围( 在函数或者让块中例如),则不需要使用 global 关键字来分配父作用域。

    @where

    选择行子集。

    @where(df, :x.>1)@where(df, :x.> x)@where(df, :x.> x, :y.==3) # the two expressions are"and-ed"

    @select

    列选择和转换也可以用于关联类型。

    @select(df, :x, :y, :z)@select(df, x2 =2*:x, :y, :z)

    @transform

    根据关键字参数添加其他列。 在函数和宏版本中都可以使用宏版本,允许直接引用使用冒号语法的列:

    transform(df, newCol =cos(df[:x]), anotherCol = df[:x]^2+3*df[:x] +4)@transform(df, newCol =cos(:x), anotherCol =:x^2+3*:x+4)

    @transform 也适用于关联类型。

    @byrow!

    在 DataFrame row-by-row上操作。 包括对控制流和 begin end 块的支持。 由于 @byrow df 引发的"环境"隐式一行 df,因这里使用正则运算符和比较来代替 @with 中的元素。!

    @byrow! df if:A>:B; :A=:B*:Cend
    let x =0.0@byrow! df beginif:A<:B x +=:B*:Cendend xend

    注意,这里需要block块来创建一个范围,允许在 @byrow 内分配 x。!

    byrow 还支持特殊语法来分配新列以使 byrow 对数据转换更有用。! 使用 Array 容器,语法 @newcol x::Array{Int} 分配一个新的列 :x,带有 elType Int。 注意,返回的AbstractDataFrame包含这些新列,但原始 df 不受影响。 下面是一个添加了两个新列的示例:

    df =DataFrame(A =1:3, B = [2, 1, 2])
    df2 =@byrow! df begin@newcol colX::Array{Float64}@newcol colY::DataArray{Int}:colX=:B==2? pi *:A : :Bif:A>1:colY=:A*:Bendend

    linq样式查询和转换

    定义了DataFrames上操作的许多函数。 下面是 hadley dplyr和通用LINQ函数的等价的表。

    
    Julia dplyr LINQ
    
    
    ---------------------------------------------
    
    
    @where filter Where
    
    
    @transform mutate Select (?)
    
    
    @by GroupBy
    
    
    @groupby group_by
    
    
    @based_on summarise/do
    
    
    @orderby arrange OrderBy
    
    
    @select select Select
    
    
    
    

    链接操作是操作数据的一种有用方法。 有几种方法可以做到这一点。 这仍然在基本的Julia ( https://github.com/JuliaLang/julia/issues/5571 ) 中。 以下是来自 Lazy.jl的一个选项,Mike Innes:

    x_thread = @>begin df
     @transform(y =10*:x)
     @where(:a.>2)
     @by(:b, meanX =mean(:x), meanY =mean(:y))
     @orderby(:meanX)
     @select(:meanX, :meanY, var =:b)end

    替代LINQ宏

    作为另一个实验,还有一个 @linq 宏支持链接和其他 MACROS 中定义的所有功能。 以下是 @linq的一个示例:

    x_thread =@linq df |>transform(y =10*:x) |>where(:a.>2) |>by(:b, meanX =mean(:x), meanY =mean(:y)) |>orderby(:meanX) |>select(:meanX, :meanY, var =:b)

    相对于单个 MACROS的使用,链接看起来更清晰,更明显,因为 @ 符号的噪音更少。 这种方法也避免了填充有限的宏 NAME 空间。 主要的缺点是在引擎盖下发生了更多的魔法。

    这里方法是可以扩展的。下面是 with的宏和 @linq 版本的比较。

    macrowith(d, body)
     esc(with_helper(d, body))endfunctionlinq(::SymbolParameter{:with}, d, body)
     with_helper(d, body)end

    上面的linq 方法注册了为所有 with() 调用定义的表达式替换方法。 它应该返回一个类似宏的表达式。

    同样,这是实验性的。基于反馈,我们可以决定只使用 @linq 或者只支持宏集。

    在上的操作

    现在包含以下操作:

    • where(g, d -> mean(d[:a])> 0)@where(g, mean(:a)> 0) 基于给定条件的--滤波器组。 返回一个 GroupedDataFrame。

    • orderby(g, d -> mean(d[:a])) 基于给定条件的@orderby(g, mean(:a)) --排序组。 返回一个 GroupedDataFrame。

    • DataFrame(g) --将分组转换为具有相同组排序的DataFrame。

    • @based_on(g, z = mean(:a)) --在组中汇总结果。 返回一个 DataFrame。

    • transform(g, d -> y = d[:a] - mean(d[:a]))@transform(g, y = :a - mean(:a)) --根据组中的操作转换 DataFrame。 返回一个 DataFrame。

    你还可以在GroupedDataFrames上索引。 g[1] 是第一个组,作为SubDataFrame返回。 g[[1,4,5]] 或者 g[[true, false, true, false, false]] 将组的子集作为GroupedDataFrame返回。 你还可以遍历 GroupedDataFrames。

    最通用的split-apply-combine方法是基于 map的。 map(fun, g) 返回一个带有键和变量不变的GroupApplied对象。 这可以与 combine 一起使用。

    性能

    @with 通过解析符号( 比如 ) 指示的所有列的表达式体来工作。 :colA ) 然后创建一个函数,该函数将该正文包装并将列作为函数参数传递。 这个函数被称为。 操作是有效的,因为:

    • 定义了伪匿名函数,因此类型是稳定的。
    • 列作为引用传递,从而消除了DataFrame索引。

    所有其他 MACROS 都基于 @with

    CompositeDataFrame

    CompositeDataFrame 是使用复合类型构建的稳定的AbstractDataFrame。 每个列都是复合类型中的字段。 CompositeDataFrame 是抽象类型;每个具体的复合类型都继承自。 这种方法的优点是:

    • 你可以使用 df.colA 直接访问单个列。 这里类型稳定,因此代码应该更快。 ( 仍然需要考虑函数边界。)

    • 当前可以执行所有索引操作。

    一些缺点包括:

    • 由于滥用了类型系统,为 CompositeDataFrame 创建一种新的类型可能会浪费内存。

    • 你不能在创建 CompositeDataFrame 后更改它的结构。 它几乎是一个不可变的对象。 例如要添加一列,你需要执行以下操作:

    transform(df, newcol = df.colA +5)

    这样做的好处是,API变得更加功能化。 对 CompositeDataFrame的所有操作都返回一个新对象。 通常,这不会造成更多的内存。

    若要创建 CompositeDataFrame,请使用 CompositeDataFrame:

    n =10d =CompositeDataFrame(a =1:n, b =rand(10), c =rand(1:3, n))

    还可以通过将 CompositeDataFrame的类型包括为第一个符号来对它的进行 NAME:

    n =10d =CompositeDataFrame(:MyDF, a =1:n, b =rand(n), c =rand(1:3, n))

    你还可以手动定义 CompositeDataFrame,如下所。 如果这样做,则负责保持每一列的长度相同。

    immutable MyDF <:AbstractCompositeDataFrame a::Vector{Int} b::Vector{Float64} c::DataVector{Float64}endMyDF(n::Integer) =MyDF(zeros(Int, n), zeros(n), zeros(n))
    d =MyDF(10)

    注意,CompositeDataFramedf.colA的字段访问稳定,但不能像 df[:colA] 那样使用 getindex 索引。 df[:colA] 工作正常,但它不稳定。

    使用 row(d, i) 或者 iterator eachrow(d) 也提供了对行的稳定访问。 以下是一个示例:

    n =10d =CompositeDataFrame(:MyDF, a =1:n, b =rand(10), c =DataArray(rand(1:3, n)))
    x =row(d, 5)
    x.a # 5y = [x.a * x.b for x ineachrow(d)]

    在上面的例子中,调用 CompositeDataFrame 创建了包含复合数据框架的类型 MyDF,以及 roweachrow 使用的另一个类型 MyDFRow

    包维护

    Tom 是领先维护者。 任何 JuliaStats协作者都有写访问权限,并且可以接受请求请求。

    欢迎请求受欢迎。请求请求应包括已经更新的测试。 如果功能更改,则应添加或者更新日志文件。 通常,遵循 DataFrames中的指南。


    相关文章