Julia 朱莉娅:从表达式创建数据帧列?
鉴于此:Julia 朱莉娅:从表达式创建数据帧列?,julia,dataframesmeta.jl,Julia,Dataframesmeta.jl,鉴于此: dict = Dict(("y" => ":x / 2")) df = DataFrame(x = [1, 2, 3, 4]) df 4×1 DataFrame │ Row │ x │ │ │ Int64 │ ├─────┼───────┤ │ 1 │ 1 │ │ 2 │ 2 │ │ 3 │ 3 │ │ 4 │ 4 │ 我想说: 4×2 DataFrame │ Row │
dict = Dict(("y" => ":x / 2"))
df = DataFrame(x = [1, 2, 3, 4])
df
4×1 DataFrame
│ Row │ x │
│ │ Int64 │
├─────┼───────┤
│ 1 │ 1 │
│ 2 │ 2 │
│ 3 │ 3 │
│ 4 │ 4 │
我想说:
4×2 DataFrame
│ Row │ x │ y │
│ │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1 │ 1 │ 0.5 │
│ 2 │ 2 │ 1.0 │
│ 3 │ 3 │ 1.5 │
│ 4 │ 4 │ 2.0 │
这似乎是DataFramesMeta
的完美应用程序,无论是@with
还是@eachrow
,但我无法在存在:x
的环境中使表达式按预期进行计算
基本上,我希望能够在dict
中迭代(k,v)
对,并为每个符号(k)
创建一个新列,其中包含相应的值eval(Meta.parse(v))
,或者类似的内容,在进行评估时,存在符号
如:x
我没想到这会起作用,它也不会:
[df[Symbol(k)] = eval(Meta.parse(v)) for (k, v) in dict]
ERROR: MethodError: no method matching /(::Symbol, ::Int64)
但这说明了问题:我需要在表达式包含符号的环境中对表达式求值
但是,使用将其移动到@中不起作用:
using DataFramesMeta
@with(df, [eval(Meta.parse(v)) for (k, v) in dict])
ERROR: MethodError: no method matching /(::Symbol, ::Int64)
使用@eachrow
失败的方式相同:
using DataFramesMeta
@eachrow df begin
for (k, v) in dict
@newcol tmp::Vector{Float32}
tmp = eval(Meta.parse(v))
end
end
ERROR: MethodError: no method matching /(::Symbol, ::Int64)
我猜我还不清楚DataFramesMeta
是如何在数据帧中创建环境的。我也不必使用DataFramesMeta
为此,任何合理简洁的选项都可以使用,因为我可以将其封装在一个包函数中
注意:我控制要解析为表达式的字符串的格式,但我希望避免复杂性,例如在字符串中指定DataFrame对象的名称,或广播每个操作。我希望初始字符串中的表达式语法对非Julia程序员来说相当清晰
更新:我在对这个问题的评论中尝试了所有三种解决方案,但它们都有一个问题:它们在函数内部不工作
dict = Dict(("y" => ":x / 2"))
data = DataFrame(x = [1, 2, 3, 4])
function transform_from_dict(df, dict)
new = eval(Meta.parse("@transform(df, " * join(join.(collect(dict), " = "), ", ") * ")"))
return new
end
transform_from_dict(data, dict)
ERROR: UndefVarError: df not defined
或:
好的,把所有评论者的答案结合起来是有效的
using DataFrames
using DataFramesMeta
dict = Dict(("y" => ":x / 2"))
data = DataFrame(x = [1, 2, 3, 4])
将@与一起使用的方法:
# using @with
function transform_from_dict1(df, dict)
global df
[df[!, Symbol(k)] = eval(:(@with(df, $(Meta.parse(v))))) for (k, v) in dict]
return df
end
transform_from_dict1(data, dict)
# 4×2 DataFrame
# │ Row │ x │ y │
# │ │ Int64 │ Float64 │
# ├─────┼───────┼─────────┤
# │ 1 │ 1 │ 0.5 │
# │ 2 │ 2 │ 1.0 │
# │ 3 │ 3 │ 1.5 │
# │ 4 │ 4 │ 2.0 │
和的方法使用@transform
:
# using @transform
function transform_from_dict2(df, dict)
global df
new_df = eval(Meta.parse("@transform(df, " * join(join.(collect(dict), " = "), ", ") * ")"))
return new_df
end
transform_from_dict2(data, dict)
# 4×2 DataFrame
# │ Row │ x │ y │
# │ │ Int64 │ Float64 │
# ├─────┼───────┼─────────┤
# │ 1 │ 1 │ 0.5 │
# │ 2 │ 2 │ 1.0 │
# │ 3 │ 3 │ 1.5 │
# │ 4 │ 4 │ 2.0 │
两者都包含使用global
的修复程序
请注意,第二个表单使用的内存大约是第一个表单的2.5倍,这可能是由于创建了第二个数据帧:
julia> @allocated transform_from_dict1(data, dict)
853948
julia> @allocated transform_from_dict2(data, dict)
22009111
我还认为第一种形式更清晰一些,所以这就是我在内部使用的
请注意,如果您的转换中有逻辑运算符,您可能需要广播这些运算符,并且像往常一样,您需要提前处理任何丢失的数据问题。我与@Ajar并行处理了这个答案,没有从该答案复制任何内容,我也不知道它。我对Julia完全是新手,所以我不得不安装它(因为我认为在线编译器甚至不知道数据帧),后来我明白了这些包无论如何都必须在开始时调用,无论是在线还是离线。我已经添加了初学者可能需要知道的软件包信息
using Pkg
Pkg.add("DataFrames")
Pkg.add("DataFramesMeta")
using DataFrames
using DataFramesMeta
dict = Dict(("y" => ":x / 2"))
df = DataFrame(x = [1, 2, 3, 4])
解决方案:
julia> function transform_from_dict!(k, v)
global df
df[!, Symbol(k)] = eval(:(@with(df, $(Meta.parse(v)))))
return nothing
end
julia> function transform_from_dict(df, dict)
global new
new = eval(Meta.parse("@transform(df, " * join(join.(collect(dict), " = "), ", ") * ")"))
return new
end
@transform解决方案:
julia> function transform_from_dict!(k, v)
global df
df[!, Symbol(k)] = eval(:(@with(df, $(Meta.parse(v)))))
return nothing
end
julia> function transform_from_dict(df, dict)
global new
new = eval(Meta.parse("@transform(df, " * join(join.(collect(dict), " = "), ", ") * ")"))
return new
end
感谢其他评论员,在@Ajar的答案中列出了一些基本的想法。尝试[df[!,Symbol(k)]=eval(DataFramesMeta.with_helper(df,Meta.parse(v)))for dict中的(k,v)
或[df[!,Symbol(k)]=eval(:(@with(df,$(Meta.parse(v);))))for dict中的(k,v)
。但两者都很棘手。关键是你不想直接eval
v
,而是让DataFramesMeta
用它的魔力来评估它。一般来说,我不建议做这样的操作,因为它们是不安全的。如果您确实需要这样做,例如,这将起作用eval(Meta.parse(@transform(df),*join(join.(collect(dict),“=”,”,“*”)))
。它们将在函数内部失败,因为eval
是在模块的全局范围内计算的。DataFramesMeta.jl不创建eval
环境,但其宏在编译时而不是在运行时解析。在脚本开头添加global new
,并在函数开头(函数内部)添加global new
。这是Pyton中的一个通用技巧,适用于任何需要在运行时读取并且仍然随时间增长的对象。看见也许它在朱莉娅身上也起作用。然后df可以扩展到任何列表/目录理解。未经测试:可能它还可以与eval()+Meta.parse()结合使用。顺便说一句:我没有看到他是茱莉亚而不是Python,请把这个想法转给茱莉亚。你接受我的回答太公平了。谢谢你,你也得到了明确的赞成票,因为你不能指望我回答,因为我对朱莉娅来说是全新的。谢谢你的公平竞争。再次投票支持时间测试。
1-element Array{Nothing,1}:
nothing
julia> df
4×2 DataFrame
Row │ x y
│ Int64 Float64
─────┼────────────────
1 │ 1 0.5
2 │ 2 1.0
3 │ 3 1.5
4 │ 4 2.0
julia> function transform_from_dict(df, dict)
global new
new = eval(Meta.parse("@transform(df, " * join(join.(collect(dict), " = "), ", ") * ")"))
return new
end
transform_from_dict (generic function with 1 method)
julia>
julia> transform_from_dict(data, dict)
4×2 DataFrame
Row │ x y
│ Int64 Float64
─────┼────────────────
1 │ 1 0.5
2 │ 2 1.0
3 │ 3 1.5
4 │ 4 2.0