Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor plot.xts() internals #408

Closed
joshuaulrich opened this issue Nov 17, 2023 · 0 comments
Closed

Refactor plot.xts() internals #408

joshuaulrich opened this issue Nov 17, 2023 · 0 comments
Assignees
Labels
Milestone

Comments

@joshuaulrich
Copy link
Owner

Original design

plot.xts() is based on quantmod::chart_Series(). The internal structures have a lot of implicit assumptions and some duplicated code, which makes the code harder to reason about and change. For example:

  • There are no explicit "panels", i.e. what is added when addSeries() is called with on = NA. Instead, there is one list of "frames" that contains header and series frames. The odd numbered list elements are the header frames and the even numbered elements are the series frames.
    • Target: store the header and series frames in a panel object.
  • Everything that gets rendered is in one list of "actions". Where the action is rendered is stored in the "frame" attribute of the action.
    • Target: frames should contain actions that are rendered in the the frame.
  • There are separate lists for the ylim and x/y aspect ratio for each frame.
    • Target: frames should contain their ylim and x/y aspect ratio.
  • There are 5 separate code blocks to add y-axis grid lines and labels.
    • Target: the panel should control the y-axis settings.
  • Some functions are always called, even though they're only necessary in certain situations. For example, get_ylim() is always called but only needed when multi.panel = TRUE.
  • The x-axis action is part of the first series frame.
    • Target: the x-axis is the same for the entire plot, so it should be part of the plot object.

The main purpose for this refactor is to make it easier to implement y-axis log scaling (#103).

Refactor target design

The target design attempts to organize the plot components into their respective parts: the plot window, the window panels, and the panel data/actions.

  • The plot object contains the plot title/header (and optional timespan), the x-axis labels and tick marks, and a list of 'panel' objects. The main plot object contains:

    • Env: an environment to hold all the plot information
    • add_main_header(): add the main plot header
    • add_main_xaxis(): add the x-axis labels and ticks to the main plot
    • new_panel(): create a new panel and add it to the plot
    • get_xcoords(): get the x-coordinate values for the plot
    • get_panel(): get a specific panel
    • get_last_action_panel(): get the panel that had the last rendered action
    • new_environment(): create a new environment with Env as its parent
    • Functions that aren't intended to be called externally:
      • update_panels(): re-calculate the x-axis and y-axis values
      • render_panels(): render all the plot panels
      • x_grid_lines(): plot the x-axis grid lines
      • create_ylim(): create y-axis max/min, to handle when max(x) == min(x).
  • The panel object contains:

    • id: the numeric index of the panel in the plot's list of panels
    • asp: the x/y aspect ratio for the panel (relative vertical size)
    • ylim: the ylim of the panel when it was created
    • ylim_render: the ylim of the panel to use when rendering
    • use_fixed_ylim: when TRUE, do not update the panel ylim based on all panel series
    • header: the panel title
    • actions: a list of expressions used to render the panel
    • add_action(): add an action to the actions list
    • yaxis_expr the expression to render the y-axis min/max values, labels, and grid lines/ticks. It also contains the x-axis grid expression because we need the y-axis min/max values to know where to draw the x-axis grid lines on the panel.
@joshuaulrich joshuaulrich added this to the 0.13.2 milestone Nov 17, 2023
@joshuaulrich joshuaulrich self-assigned this Nov 17, 2023
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Nov 24, 2024
# xts 0.14.x (202x-xx-xx)

* `plot.xts()` now renders all panels when 'x' has more than 8 columns and
  `multi.panel = TRUE`. Columns 9 and later didn't render because the default
  of `plot.xts()` is 'col = 1:8'. Thanks to Ethan Smith for the report and
  patch.
  ([#423](joshuaulrich/xts#423))
  ([#424](joshuaulrich/xts#424))

* `plot.xts()` no longer errors when 'ylim' is constant and negative. Thanks
  to Ethan Smith for the report.
  ([#422](joshuaulrich/xts#422))

* Do not use `SET_TYPEOF()` in C because it is not part of the public R API.

* `merge.xts()` no longer converts 'x' or 'y' from double to integer in the C
  code when they are not used in the result. This avoids an unnecessary and
  confusing warning. Thanks to Jeff Ryan for the report.

# xts 0.14.0 (2024-06-05)

* `addEventLines()` and `addLegend()` now draw on multiple panels when `on` is
  a vector. Thanks to Ethan Smith for the report.
  ([#420](joshuaulrich/xts#420))

* Replace `SET_TYPEOF()` in merge.c because it will error when it tries to
  convert a REAL to an INTEGER. Thanks to Kurt Hornik for the report!
  ([#419](joshuaulrich/xts#419))

* Fix crash when 'j' is not an integer and in [0, 1) (e.g. `j = 0.1`). Also
  throw a warning when 'j' is not an integer.
  ([#413](joshuaulrich/xts#413))
  ([#415](joshuaulrich/xts#415))

* Fix plot header when `layout()` is used to draw multiple plots on a single
  device. Thanks to Dirk Eddelbuettel for the report and testing!
  ([#412](joshuaulrich/xts#412))

* Fix plot legend location when the y-axis is log scale.
  ([#407](joshuaulrich/xts#407))

# xts 0.13.2 (2024-01-21)

* Print a message when `period.apply()` is called with `FUN = mean` because it
  calculates the mean for each column, not all the data in the subset like it
  does for all other functions. The message says to use `FUN = colMeans` for
  current behavior and `FUN = function(x) mean(x)` to calculate the mean for
  all the data. This information is also included in the help files. The option
  `xts.message.period.apply.mean = FALSE` suppresses the message.
  ([#124](joshuaulrich/xts#124))

* Fix error when `print.xts()` is called 'quote' or 'right' arguments.
  ([#401](joshuaulrich/xts#401))

* Fix `addPolygon()` so it renders when `observation.based = TRUE`.
  ([#403](joshuaulrich/xts#403))

* Print trailing zeros for index value with fractional seconds, so every index
  value has the same number of characters.
  ([#404](joshuaulrich/xts#404))

* Add ability to log scale the y-axis in `plot.xts()`.
  ([#103](joshuaulrich/xts#103))

* Actually change the underlying index values when 'tclass' is changed from a
  class with a timezone (e.g. POSIXct) to one without a timezone (e.g. Date).
  Add a warning when this happens, with a global option to always suppress the
  warning.
  ([#311](joshuaulrich/xts#311)).

* Significantly refactor the internals of `plot.xts()`.
  ([#408](joshuaulrich/xts#408))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant