Skip to content
New issue

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

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

Already on GitHub? # to your account

implement vis_dat_ly, extending from this code #36

Open
njtierney opened this issue Jan 8, 2017 · 6 comments
Open

implement vis_dat_ly, extending from this code #36

njtierney opened this issue Jan 8, 2017 · 6 comments

Comments

@njtierney
Copy link
Collaborator

vis_dat_ly is not working at the moment, for reasons that I don't fully understand, so I'm going to dump the code here so I don't forget it. I would like to avoid unused, untested code in visdat.

#' Produces an interactive visualisation of a data.frame to tell you what it contains.
#'
#' \code{vis_dat_ly} uses plotly to provide an interactive version of vis_dat, providing an at-a-glance plotly object of what is inside a dataframe. Cells are coloured according to what class they are and whether the values are missing.
#'
#' @param x a \code{data.frame}
#'
#' @return a \code{plotly} object
#'
#' @examples
#'
#' \dontrun{
#' # currently does not work, some problems with palletes and other weird messages.
#' vis_dat_ly(airquality)
#'
#'}
#'
#'
vis_dat_ly <- function(x) {

  # x = data.frame(x = 1L:10L,
  #                y = letters[1:10],
  #                z = runif(10))

  # apply the fingerprint function to get the class
  d <- x %>% purrr::dmap(fingerprint) %>% as.matrix()

  # heatmap fails due to not being a numeric matrix
  # heatmap(d)

  # plotly fails due to the number of colours being too many?
  plotly::plot_ly(z = d,
                  type = "heatmap")


}
njtierney added a commit that referenced this issue Jan 8, 2017
@njtierney
Copy link
Collaborator Author

OK, here is the current progress

library(visdat)
library(magrittr)
x = data.frame(x = 1L:10L,
               y = letters[1:10],
               z = runif(10))

d <- x %>%
  purrr::map_df(visdat:::fingerprint) %>%
  dplyr::mutate(rows = seq_len(nrow(.))) %>%
  tidyr::gather_(key_col = "variable",
                 value_col = "valueType",
                 gather_cols = names(.)[-length(.)]) %>%
  # dplyr::mutate(value = vis_extract_value_(x))
  dplyr::mutate(value = dplyr::case_when(
    valueType == "integer" ~ 1L,
    valueType == "factor" ~ 2L,
    valueType == "numeric" ~ 3L
  ))

plotly::plot_ly(d,
                x = ~variable,
                y = ~rows,
                z = ~value) %>%
  plotly::add_heatmap()

From my experimentation, It appears that I need to provide a numeric number for the "class" - I can't use the categorical class. Unless @cpsievert has any thoughts?

Carson, some context: I'm working on making the vis_* family fully in plot_ly, as calling ggplot2::ggplotly is awesome, but slow for these kind of plots.

Note - taking examples from: https://plotly-book.cpsievert.me/d-frequencies.html

@cpsievert
Copy link
Contributor

cpsievert commented Aug 17, 2017

If it were me, I'd try using heatmapgl (for performance) with showscale=FALSE and a custom colorscale (see fig 2.5 here). Then, for a "legend", I'd use shapes & annotations

@njtierney
Copy link
Collaborator Author

See #25 for reference, closing that issue to avoid duplication

@njtierney
Copy link
Collaborator Author

Here is another attempt at this, I don't have time to fix this up for the 0.5.0 release.

library(visdat)
library(magrittr)
x <- data.frame(x = 1L:10L,
                y = letters[1:10],
                z = runif(10))
n <- nrow(x)
rows <- rep(1:nrow(x),ncol(x))
vars <- rep(colnames(x), each = n)

txt <- matrix(paste(sprintf("value = %s", as.matrix(x)),
                    sprintf("variable = %s", vars),
                    sprintf("row = %s", rows),
                    sep = "<br />"),
              nrow = nrow(x))

d <- x %>%
  purrr::map_df(visdat:::fingerprint) %>%
  dplyr::mutate(rows = seq_len(nrow(.))) %>%
  tidyr::gather_(key_col = "variable",
                 value_col = "valueType",
                 gather_cols = names(.)[-length(.)]) %>%
  # dplyr::mutate(value = vis_extract_value_(x))
  dplyr::mutate(value = dplyr::case_when(
    valueType == "integer" ~ 1L,
    valueType == "factor" ~ 2L,
    valueType == "numeric" ~ 3L
  ))

# get class++ - classes plus is it missing?
whatsit <- function(x){
  dplyr::if_else(condition = is.na(x),
                 true = "NA",
                 false = class(x))
}

whatsit_v <- Vectorize(whatsit)

what_is_it_really <- whatsit_v(x)

categories <- unique(as.character(what_is_it_really))

n_categories <- length(categories)


discretize_colorscale <- function(palette, granularity = 100) {
  n <- length(palette)
  colorscale <- data.frame(range = seq(0, n, length.out = n*granularity),
                           color = rep(palette, each = granularity))
  
  setNames(colorscale, NULL)
}


plotly::plot_ly(d,
                x = ~variable,
                text = txt,
                y = ~rows,
                z = ~value,
                colorscale = n_categories,
                type = "heatmap",
                colorscale = discretize_colorscale(
                  palette = viridisLite::viridis(n_categories),
                  granularity = 20000
                )
) %>%
  plotly::colorbar(tickmode = "array",
                   ticktext = c(categories),
                   tickvals = 1:3,
                   len = 0) %>%
  plotly::layout(xaxis = list(side = "top"),
                 yaxis = list(autorange = "reversed"),
                 legend = list(orientation = 'h')
  )

Created on 2018-06-04 by the reprex package (v0.2.0).

going to move this to version 0.6.0 for the moment - add a note to remove this function from release at #81

@cpsievert
Copy link
Contributor

cpsievert commented Jun 4, 2018

I think you want

range = seq(0, 1, length.out = n*granularity),

not

range = seq(0, n, length.out = n*granularity),

also, here is another way to do this with a legend instead of a colorbar:

library(plotly)
library(htmlwidgets)

pal <- viridisLite::viridis(n_categories)
cols <- discretize_colorscale(
  palette = pal,
  granularity = 20000
)


p <- plot_ly() 

for (i in seq_along(categories)) {
  p <- add_markers(
    p, x = names(x)[[1]], y = 1, color = I(pal[[i]]), 
    name = categories[[i]], hoverinfo = "none", symbol = I(15),
    visible = "legendonly"
  )
}

p <- add_heatmap(
    p, data = d,
    x = ~variable,
    text = txt,
    y = ~rows,
    z = ~value,
    colorscale = cols,
    showscale = F
  ) %>%
  layout(
    xaxis = list(side = "top"),
    yaxis = list(autorange = "reversed"),
    legend = list(orientation = "h")
  )


# disable legend clicking https://github.com/plotly/plotly.js/issues/665
onRender(p, "
  function(el, x) {
    el.on('plotly_legendclick', function(x) { return false; })
  }
")

@njtierney
Copy link
Collaborator Author

Thanks for that, Carson - really appreciate it!

This looks much better, although there are some issues with NA values not appearing on mouseover - I think that this would have to do with the code I wrote that creates txt.

I will come back to this at another time for version 0.6.0

library(plotly)
#> Loading required package: ggplot2
#> 
#> Attaching package: 'plotly'
#> The following object is masked from 'package:ggplot2':
#> 
#>     last_plot
#> The following object is masked from 'package:stats':
#> 
#>     filter
#> The following object is masked from 'package:graphics':
#> 
#>     layout
library(htmlwidgets)

# x <- data.frame(x = 1L:10L,
#                 y = letters[1:10],
#                 z = runif(10))

x <- airquality

n <- nrow(x)
rows <- rep(1:nrow(x),ncol(x))
vars <- rep(colnames(x), each = n)

# get class++ - classes plus is it missing?
whatsit <- function(x){
  dplyr::if_else(condition = is.na(x),
                 true = "NA",
                 false = class(x))
}

whatsit_v <- Vectorize(whatsit)

what_is_it_really <- whatsit_v(x)

categories <- unique(as.character(what_is_it_really))

n_categories <- length(categories)

pal <- viridisLite::viridis(n_categories)

discretize_colorscale <- function(palette, granularity = 100) {
  n <- length(palette)
  colorscale <- data.frame(range = seq(from = 0, 
                                       to = 1, 
                                       length.out = n*granularity),
                           color = rep(palette, each = granularity))
  
  setNames(colorscale, NULL)
}


cols <- discretize_colorscale(
  palette = pal,
  granularity = 20000
)

txt <- matrix(paste(sprintf("value = %s", as.matrix(x)),
                    sprintf("variable = %s", vars),
                    sprintf("row = %s", rows),
                    sep = "<br />"),
              nrow = nrow(x))


p <- plot_ly() 

for (i in seq_along(categories)) {
  p <- add_markers(
    p, x = names(x)[[1]], y = 1, color = I(pal[[i]]), 
    name = categories[[i]], hoverinfo = "none", symbol = I(15),
    visible = "legendonly"
  )
}

d <- x %>%
  purrr::map_df(visdat:::fingerprint) %>%
  dplyr::mutate(rows = seq_len(nrow(.))) %>%
  tidyr::gather_(key_col = "variable",
                 value_col = "valueType",
                 gather_cols = names(.)[-length(.)]) %>%
  # dplyr::mutate(value = vis_extract_value_(x))
  dplyr::mutate(value = dplyr::case_when(
    valueType == "integer" ~ 1L,
    valueType == "factor" ~ 2L,
    valueType == "numeric" ~ 3L
  ))


p <- add_heatmap(
  p, data = d,
  x = ~variable,
  text = txt,
  y = ~rows,
  z = ~value,
  colorscale = cols,
  showscale = F
) %>%
  layout(
    xaxis = list(side = "top"),
    yaxis = list(autorange = "reversed"),
    legend = list(orientation = "h")
  )


# disable legend clicking https://github.com/plotly/plotly.js/issues/665
onRender(p, "
  function(el, x) {
    el.on('plotly_legendclick', function(x) { return false; })
  }
")

Created on 2018-06-05 by the reprex package (v0.2.0).

@njtierney njtierney added this to the V0.6.0 milestone Jun 6, 2018
@njtierney njtierney removed the V0.6.0 label Jun 6, 2018
@njtierney njtierney modified the milestones: V0.6.0, V0.7.0 Jun 6, 2018
@njtierney njtierney modified the milestones: V0.7.0, V0.6.0 Oct 14, 2022
@njtierney njtierney modified the milestones: 0.7.0, 0.8.0 Apr 24, 2023
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

2 participants