在Julia中,可以使用StatsPlots.jl
绘制方框图。假设有一个名为df
的DataFrame
,我们可以通过以下方式为它的一列a
绘制方框图:
julia> @df df boxplot(["a"], :a, fillalpha=0.75, linewidth=2)
我想把相同的结构放在一个函数中:
julia> function BoxPlotColumn(col::Union{Symbol, String}, df::DataFrame)
if isa(col, String)
@df df boxplot([col], Symbol(col), fillalpha=0.75, linewidth=2)
else
@df df boxplot([String(col)], col, fillalpha=0.75, linewidth=2)
end
end
BoxPlotColumn (generic function with 1 method)
然后,如果我说BoxPlotColumn("a", df)
,Julia抛出一个错误:
ERROR: Cannot convert Symbol to series data for plotting
Stacktrace:
[1] error(s::String)
@ Base .error.jl:35
[2] _prepare_series_data(x::Symbol)
@ RecipesPipeline C:UsersShayan.juliapackagesRecipesPipelineOXGmHsrcseries.jl:8
[3] _series_data_vector(x::Symbol, plotattributes::Dict{Symbol, Any})
@ RecipesPipeline C:UsersShayan.juliapackagesRecipesPipelineOXGmHsrcseries.jl:35
[4] macro expansion
@ C:UsersShayan.juliapackagesRecipesPipelineOXGmHsrcseries.jl:135 [inlined]
[5] apply_recipe(plotattributes::AbstractDict{Symbol, Any}, #unused#::Type{RecipesPipeline.SliceIt}, x::Any, y::Any, z::Any)
@ RecipesPipeline C:UsersShayan.juliapackagesRecipesBaseqpxEXsrcRecipesBase.jl:289
[6] _process_userrecipes!(plt::Any, plotattributes::Any, args::Any)
@ RecipesPipeline C:UsersShayan.juliapackagesRecipesPipelineOXGmHsrcuser_recipe.jl:36
[7] recipe_pipeline!(plt::Any, plotattributes::Any, args::Any)
@ RecipesPipeline C:UsersShayan.juliapackagesRecipesPipelineOXGmHsrcRecipesPipeline.jl:70
[8] _plot!(plt::Plots.Plot, plotattributes::Any, args::Any)
@ Plots C:UsersShayan.juliapackagesPlotslW9llsrcplot.jl:209
[9] #plot#145
@ C:UsersShayan.juliapackagesPlotslW9llsrcplot.jl:91 [inlined]
[10] boxplot(::Any, ::Vararg{Any}; kw::Base.Pairs{Symbol, V, Tuple{Vararg{Symbol, N}}, NamedTuple{names, T}} where {V, N, names, T<:Tuple{Vararg{Any, N}}})
@ Plots C:UsersShayan.juliapackagesRecipesBaseqpxEXsrcRecipesBase.jl:410
[11] add_label(::Vector{String}, ::typeof(boxplot), ::Vector{String}, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Real, Tuple{Symbol, Symbol}, NamedTuple{(:fillalpha, :linewidth), Tuple{Float64, Int64}}}) @ StatsPlots C:UsersShayan.juliapackagesStatsPlotsfaFN5srcdf.jl:153
[12] (::var"#33#34"{String})(349::DataFrame)
@ Main .none:0
[13] BoxPlotColumn(col::String, df::DataFrame)
@ Main c:UsersShayanDocumentsPython Scriptstest2.jl:15
[14] top-level scope
@ c:UsersShayanDocumentsPython Scriptstest2.jl:22
正因为如此:@df df boxplot([col], Symbol(col), fillalpha=0.75, linewidth=2)
我该怎么解决这个问题为什么会发生这种情况我只是在函数中写了同样的东西。
我只是在一个函数中写了同样的东西。
你没有写同样的东西。在原始代码中使用字符串和Symbol
文字,在函数中传递一个变量。这是关键的区别。
要解决此问题,我建议您使用DataFramesMeta.jl:中的@with
BoxPlotColumn(col::Union{Symbol, String}, df::DataFrame) =
@with df boxplot([string(col)], $col, fillalpha=0.75, linewidth=2)
因为@with
支持使用$
以编程方式处理列名。
编辑
为什么我们说
boxplot(..., col, ...)
时Julia不操作
它不工作,因为@df
和@which
都是宏。由于它们是宏,所以它们将代码转换为以后才执行的其他代码。这些宏的设计方式是,当它们看到符号文字时,例如:a
,它们会以特殊的方式处理它,并将其视为数据帧的一列。当他们看到变量col
时,他们不知道这个变量指向一个符号,因为在计算代码之前执行宏(记住,宏是一种在执行此代码之前将代码转换为其他代码的方法(。看见https://docs.julialang.org/en/v1/manual/metaprogramming/#man-宏
MethodError:没有与isfinite(:String15(匹配的方法
很可能您有一个包含字符串而非数字的列,而是编写例如names(df, Real)
,以仅获得存储实数的列的列表(没有遗漏(。如果您希望允许丢失,则编写names(df, Union{Missing,Real})
。