如何解决Julia DataFrame - 根据另一列中的值更新一列中的值的首选方法
我正在尝试学习 Julia 中根据另一列中的值更新一列中的 DataFrame 的最佳方法。我主要使用 R,我可以在其中编写:
library(data.table)
dt <- data.table(A = 1:5,B = 6:10)
# A B
# ----
# 1 6
# 2 7
# 3 8
# 4 9
# 5 10
dt[A < 3,B := B * 2]
# A B
# ----
# 1 12
# 2 14
# 3 8
# 4 9
# 5 10
...它在 B
的位置更新 A < 3
,而且非常简洁。我发现的唯一 Julia 等效项是:
using DataFrames
df = DataFrame(A = 1:5,B = 6:10)
transform!(df,[:A,:B] => ByRow((a,b) -> a < 3 ? b * 2 : a))
这是唯一的方法吗?它是首选吗?是否有任何软件包可以使这更简洁?
解决方法
目前您可以这样做:
@. df.B = ifelse(df.A < 3,2*df.B,df.B)
或者这个
df.B = @. ifelse(df.A < 3,df.B)
或者这个
dfv = view(df,df.A .< 3,:);
@. dfv.B = dfv.B * 2
第三种方法类似于 data.table 提供的方法,但您必须首先单独创建一个视图(data.table 在一个命令中执行的操作)。
第一种和第二种方法的区别在于 :B
列是就地更新还是替换。
虽然 Bogumił Kamiński 是这里的权威,his answer 展示了一个非常好的、简短的替代解决方案,但我相信还有更多讨论和示例的空间。
首先,您的原始解决方案肯定是惯用的,在很多情况下我个人更喜欢它。变异是高性能语言的重要工具,但它也是许多问题的根源。 Julia 约定(在某些其他语言中也很常见)在改变参数的函数中添加感叹号(或“砰!”),这在很大程度上有助于跟踪发生变异的位置。
就地赋值 (.=
) 在 Julia 代码中也很常见,但在我自己的代码和许多库作者的代码中,这些通常会在按照约定命名的函数中找到,以便意图在通话现场很清楚。如果你发现你经常需要你描述的那种操作,你可以写一个函数:
function filter_transform!(df,pred,args...)
fdf = filter(pred,df; view=true)
fdf .= transform(copy(fdf),(col => f => col for (col,f) in args)...)
end
并使用它:
filter_transform!(df,:A => <(3),:B => b -> b*2)
我觉得读起来不错。该函数适用于您的原始示例,但不允许分配给另一列或在计算中使用多列或添加列(如 data.table
允许,NA
填充新列的跳过行)。这个稍微复杂的函数实现了:
applyif(cond,f,x,default) = cond ? f(x) : default
function filter_transform!(df,filt_cols,args...)
colset = Set(keys(eachcol(df)))
fcols,pred = filt_cols
for (cols,(f,assign)) in args
df[!,assign] .= in(assign,colset) ? df[!,assign] : missing
@. df[!,assign] = applyif(pred(df[!,fcols]),df[!,cols],assign])
end
end
使用:
filter_transform!(df,:B => (b -> b*2) => :B)
最后,您询问了软件包,确实有您可以查看的软件包:
Query.jl(此示例重新分配而不是就地修改):
using Query
df = df |> @mutate(B = _.A < 3 ? _.B*2 : _.B)
DataFramesMeta(也重新分配):
using DataFramesMeta
df = @eachrow df :B = :A < 3 ? :B*2 : :B
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。