Giter Club home page Giter Club logo

Comments (6)

avishah3 avatar avishah3 commented on June 11, 2024

Hi, I would like to work on this, do you mind assigning it to me? I am new to open-source contributions so any guidance on where to locate the relevant code would be really appreciated. Thanks!

from plots.jl.

BeastyBlacksmith avatar BeastyBlacksmith commented on June 11, 2024

I think for a start we can add the handling of this to the bar recipe here:

Plots.jl/src/recipes.jl

Lines 407 to 507 in fcf2e74

@recipe function f(::Type{Val{:bar}}, x, y, z) # COV_EXCL_LINE
ywiden --> false
procx, procy, xscale, yscale, _ = _preprocess_barlike(plotattributes, x, y)
nx, ny = length(procx), length(procy)
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
cv = map(xi -> discrete_value!(plotattributes, :x, xi)[1], procx)
procx = if nx == ny
cv
elseif nx == ny + 1
0.5diff(cv) + @view(cv[1:(end - 1)])
else
error(
"bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))",
)
end
# compute half-width of bars
bw = plotattributes[:bar_width]
hw = if bw === nothing
0.5_bar_width * if nx > 1
ignorenan_minimum(filter(x -> x > 0, diff(sort(procx))))
else
1
end
else
map(i -> 0.5_cycle(bw, i), eachindex(procx))
end
# make fillto a vector... default fills to 0
if (fillto = plotattributes[:fillrange]) === nothing
fillto = 0
end
if yscale in _logScales && !all(_is_positive, fillto)
# github.com/JuliaPlots/Plots.jl/issues/4502
# https://github.com/JuliaPlots/Plots.jl/issues/4774
T = float(eltype(y))
min_y = NaNMath.minimum(y)
base = _logScaleBases[yscale]
baseline = floor_base(min_y, base)
if min_y == baseline
baseline /= base
end
fillto = map(x -> _is_positive(x) ? T(x) : T(baseline), fillto)
end
xseg, yseg = map(_ -> Segments(), 1:2)
valid_i = isfinite.(procx) .& isfinite.(procy)
for i in 1:ny
valid_i[i] || continue
yi = procy[i]
center = procx[i]
hwi = _cycle(hw, i)
fi = _cycle(fillto, i)
push!(xseg, center - hwi, center - hwi, center + hwi, center + hwi, center - hwi)
push!(yseg, yi, fi, fi, yi, yi)
end
# widen limits out a bit
expand_extrema!(axis, scale_lims(ignorenan_extrema(xseg.pts)..., default_widen_factor))
# switch back
if !isvertical(plotattributes)
xseg, yseg = yseg, xseg
x, y = y, x
end
# reset orientation
orientation := default(:orientation)
# draw the bar shapes
@series begin
seriestype := :shape
series_annotations := nothing
primary := true
x := xseg.pts
y := yseg.pts
# expand attributes to match indices in new series data
for k in _segmenting_vector_attributes _segmenting_array_attributes
if (v = get(plotattributes, k, nothing)) isa AVec
if eachindex(v) != eachindex(y)
@warn "Indices $(eachindex(v)) of attribute `$k` do not match data indices $(eachindex(y))."
end
# Each segment is 6 elements long, including the NaN separator.
# One segment is created for each non-NaN element of `procy`.
# There is no trailing NaN, so the last repetition is dropped.
plotattributes[k] = @views repeat(v[valid_i]; inner = 6)[1:(end - 1)]
end
end
()
end
# add empty series
primary := false
seriestype := :scatter
markersize := 0
markeralpha := 0
fillrange := nothing
x := procx
y := procy
()
end

from plots.jl.

avishah3 avatar avishah3 commented on June 11, 2024

I added this segment of code to recipes.jl below. I am trying to test it to see if it works as intended but every time I test through the Julia app it uses the original code instead of the modified one. If anyone could let me know how to test it or if there are any issues with the code I would really appreciate it. Thanks

  # custom bar plot function for named tuples
  @recipe function f(::Type{Val{:bar}}, nt::NamedTuple)
      names = collect(keys(nt))  # Extract names from the named tuple
      values = collect(values(nt))  # Extract values from the named tuple
  
      # Convert names to a format that can be used for plotting
      plotnames = map(string, names)
  
      x := plotnames
      y := values
      seriestype := :bar
      # Do I have to call the other bar plot function or would this work?
  end

from plots.jl.

BeastyBlacksmith avatar BeastyBlacksmith commented on June 11, 2024

Yeah, such a dispatch mechanism would be nice, but unfortunately that won't work here.
If you insert a @show x, y, z you will see, how the input gets passed to the recipe function (its likely in y).

But you probably can add a method to _preprocess_barlike or an inner function there to do a similiar thing. Alternatively we just add an if block to the recipe.

from plots.jl.

avishah3 avatar avishah3 commented on June 11, 2024

Okay thank you for the advice. I think this is the correct implementation, but let me know if anything is incorrect. Also, what is the best way of testing the code? When I test, it uses the official JuliaPlots code instead of my modified one. I looked at the JuliaPlots documentation but could not find much on this. I want to ensure that it works fine before creating a pull request.

Here is the updated function in recipes.jl:

  # create a bar plot as a filled step function
  @recipe function f(::Type{Val{:bar}}, x, y, z)  # COV_EXCL_LINE
      # check if 'y' is a named tuple and handle accordingly
      if typeof(y) == NamedTuple
          names = collect(keys(y))
          values = collect(values(y))
          plotnames = map(string, names)
          x = plotnames
          y = values
      else
          println("original called")
      end
      ywiden --> false
      procx, procy, xscale, yscale, _ = _preprocess_barlike(plotattributes, x, y)
      nx, ny = length(procx), length(procy)
      axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
      cv = map(xi -> discrete_value!(plotattributes, :x, xi)[1], procx)
      procx = if nx == ny
          cv
      elseif nx == ny + 1
          0.5diff(cv) + @view(cv[1:(end - 1)])
      else
          error(
              "bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))",
          )
      end
  
      # compute half-width of bars
      bw = plotattributes[:bar_width]
      hw = if bw === nothing
          0.5_bar_width * if nx > 1
              ignorenan_minimum(filter(x -> x > 0, diff(sort(procx))))
          else
              1
          end
      else
          map(i -> 0.5_cycle(bw, i), eachindex(procx))
      end
  
      # make fillto a vector... default fills to 0
      if (fillto = plotattributes[:fillrange]) === nothing
          fillto = 0
      end
      if yscale in _logScales && !all(_is_positive, fillto)
          # github.com/JuliaPlots/Plots.jl/issues/4502
          # https://github.com/JuliaPlots/Plots.jl/issues/4774
          T = float(eltype(y))
          min_y = NaNMath.minimum(y)
          base = _logScaleBases[yscale]
          baseline = floor_base(min_y, base)
          if min_y == baseline
              baseline /= base
          end
          fillto = map(x -> _is_positive(x) ? T(x) : T(baseline), fillto)
      end
  
      xseg, yseg = map(_ -> Segments(), 1:2)
      valid_i = isfinite.(procx) .& isfinite.(procy)
      for i in 1:ny
          valid_i[i] || continue
          yi = procy[i]
          center = procx[i]
          hwi = _cycle(hw, i)
          fi = _cycle(fillto, i)
          push!(xseg, center - hwi, center - hwi, center + hwi, center + hwi, center - hwi)
          push!(yseg, yi, fi, fi, yi, yi)
      end
  
      # widen limits out a bit
      expand_extrema!(axis, scale_lims(ignorenan_extrema(xseg.pts)..., default_widen_factor))
  
      # switch back
      if !isvertical(plotattributes)
          xseg, yseg = yseg, xseg
          x, y = y, x
      end
  
      # reset orientation
      orientation := default(:orientation)
  
      # draw the bar shapes
      @series begin
          seriestype := :shape
          series_annotations := nothing
          primary := true
          x := xseg.pts
          y := yseg.pts
          # expand attributes to match indices in new series data
          for k in _segmenting_vector_attributes  _segmenting_array_attributes
              if (v = get(plotattributes, k, nothing)) isa AVec
                  if eachindex(v) != eachindex(y)
                      @warn "Indices $(eachindex(v)) of attribute `$k` do not match data indices $(eachindex(y))."
                  end
                  # Each segment is 6 elements long, including the NaN separator.
                  # One segment is created for each non-NaN element of `procy`.
                  # There is no trailing NaN, so the last repetition is dropped.
                  plotattributes[k] = @views repeat(v[valid_i]; inner = 6)[1:(end - 1)]
              end
          end
          ()
      end
  
      # add empty series
      primary := false
      seriestype := :scatter
      markersize := 0
      markeralpha := 0
      fillrange := nothing
      x := procx
      y := procy
      ()
  end

from plots.jl.

BeastyBlacksmith avatar BeastyBlacksmith commented on June 11, 2024

To test that code you run:

using Pkg
Pkg.activate(temp=true)
Pkg.develop("Plots")

then you change the code in ~/.julia/dev/Plots and run

Pkg.activate(joinpath(homedir(), ".julia", "dev", "Plots"))
using Plots

and then your code should get used.

from plots.jl.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.