Nano Hash - криптовалюты, майнинг, программирование

Shiny: Как изменить форму и/или размер выбранной точки?

Я хотел бы изменить форму и размер щелкнутой точки на графике ниже. Как этого достичь? Для этого игрушечного сюжета я уменьшил количество баллов с первоначальных 100 000 до 2 000. Итак, ожидаемое решение должно быть хорошо масштабируемым и не отклоняться от исходного графика, т. е. все цвета до и после обновления точки клика должны быть одинаковыми.

library(shiny)
library(plotly)

df <- data.frame(X=runif(2000,0,2), Y=runif(2000,0,20), 
                 Type=c(rep(c('Type1','Type2'),600),
                        rep(c('Type3','Type4'),400)),
                 Val=sample(LETTERS,2000,replace=TRUE))

# table(df$Type, df$Val)

ui <- fluidPage(
  title = 'Select experiment',
  sidebarLayout(
    sidebarPanel(
      checkboxGroupInput("SelType", "Select Types to plot:",
                  choices = unique(df$Type),
                  selected = NA)
    ),
    mainPanel(
      plotlyOutput("plot", width = "400px"),
      verbatimTextOutput("click")
    )
  )
)

server <- function(input, output, session) {

  output$plot <- renderPlotly({
    if(length(input$SelType) != 0){
      df <- subset(df, Type %in% input$SelType)
      p <- ggplot(df, aes(X, Y, col = as.factor(Val))) + 
        geom_point()
    }else{
      p <- ggplot(df, aes(X, Y, col = as.factor(Val))) +
        geom_point()
    }
    ggplotly(p)  %>% layout(height = 800, width = 800)

  })

  output$click <- renderPrint({
    d <- event_data("plotly_click")
    if (is.null(d)) "Click events appear here (double-click to clear)" 
    else cat("Selected point associated with value: ", d$Val)
  })

}

shinyApp(ui, server)

введите здесь описание изображения

Здесь был задан связанный с этим вопрос, но такой подход выделения точки цветом не работает (когда количество уровней переменной велико, трудно жестко запрограммировать цвет, который уже может присутствовать на графике).

31.08.2017

Ответы:


1

Функция Plotly restyle здесь нам не поможет, но мы все равно можем использовать onclick событие вместе с небольшим количеством JavaScript. Код имеет приемлемую производительность для 10 000 баллов.


Мы можем получить точку, на которую нажали в JavaScript, используя:

var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];

(scatterlayer — слой, в котором расположены все элементы диаграммы рассеяния, scatter[n] — n-я диаграмма рассеяния, а point[p] — p-я точка в ней)

Теперь мы просто делаем эту точку намного больше (или любую другую форму/трансформацию, которую вы хотите):

point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');

Чтобы получить возможность вернуть все назад, мы сохраняем неизменную информацию о точке вместе с остальной информацией Plotly:

var plotly_div = document.getElementsByClassName('plotly')[0];
plotly_div.backup = {curveNumber: data.points[0].curveNumber,
                     pointNumber: data.points[0].pointNumber,
                     d: point.attributes['d'].value
                    }

и позже мы можем восстановить точку:

var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
old_point.setAttribute('d', plotly_div.backup.d);

Теперь мы можем добавить весь код в виджет plotly.

javascript <- "
function(el, x){
  el.on('plotly_click', function(data) {
    var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
    var plotly_div = document.getElementsByClassName('plotly')[0];
    if (plotly_div.backup !== undefined) {
      var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
      if (old_point !== undefined) {
        old_point.setAttribute('d', plotly_div.backup.d);
      }
    }
    plotly_div.backup = {curveNumber: data.points[0].curveNumber,
                         pointNumber: data.points[0].pointNumber,
                         d: point.attributes['d'].value,
                         style: point.attributes['style'].value
                        }

    point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
  });
}"

[...]

ggplotly(p) %>% onRender(javascript)

В качестве альтернативы вы можете создать новый элемент SVG на основе местоположения точки, в которой щелкнули, но с желаемым цветом и формой.


Вы можете попробовать это здесь без R/Shiny.

//create some random data
var data = [];
for (var i = 0; i < 10; i += 1) {
  data.push({x: [],
             y: [],
             mode: 'markers',
             type: 'scatter'});
  for (var p = 0; p < 200; p += 1) {
    data[i].x.push(Math.random());
    data[i].y.push(Math.random());
  }
}
//create the plot
var myDiv = document.getElementById('myDiv');
Plotly.newPlot(myDiv, data, layout = { hovermode:'closest'});

//add the same click event as the snippet above
myDiv.on('plotly_click', function(data) {
    //let's check if some traces are hidden

    var traces = document.getElementsByClassName('legend')[0].getElementsByClassName('traces');
    var realCurveNumber = data.points[0].curveNumber;
    for (var i = 0; i < data.points[0].curveNumber; i += 1) {
        if (traces[i].style['opacity'] < 1) {
            realCurveNumber -= 1
        }
    }

    data.points[0].curveNumber = realCurveNumber;
    var point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[data.points[0].curveNumber].getElementsByClassName('point')[data.points[0].pointNumber];
    var plotly_div = document.getElementsByClassName('plotly')[0];
    if (plotly_div.backup !== undefined) {
      var old_point = document.getElementsByClassName('scatterlayer')[0].getElementsByClassName('scatter')[plotly_div.backup.curveNumber].getElementsByClassName('point')[plotly_div.backup.pointNumber]
      if (old_point !== undefined) {
        old_point.setAttribute('d', plotly_div.backup.d);
      }
    }
    plotly_div.backup = {curveNumber: data.points[0].curveNumber,
                         pointNumber: data.points[0].pointNumber,
                         d: point.attributes['d'].value,
                         style: point.attributes['style'].value
                        }

    point.setAttribute('d', 'M10,0A10,10 0 1,1 0,-10A10,10 0 0,1 10,0Z');
  });
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv">

31.08.2017
  • После включения решения в опубликованный сценарий я заметил странное поведение (я не уверен, ожидается ли это). Когда ни один из Types не выбран и точка щелкнута, точка, по которой щелкнули, увеличивается в размере с тем же цветом. Хотя форма не меняется, она масштабируема и четко отличается от других близлежащих точек. Теперь, когда вы выбираете Type, в той же области создается новый график. Удивительно, но описанный выше эффект недоступен при щелчке по точке. Я предполагаю, что должна быть небольшая настройка, чтобы исправить это. 01.09.2017
  • Другое наблюдение заключается в том, что если мы отменим выбор выбранного типа и вернемся к исходному графику, указанная выше функция будет восстановлена. 01.09.2017
  • @Prraep: Спасибо, что поймали это! Если график перерисовывается, сохраненная информация указывает на точку, которая больше не существует, простой блок if решает проблему. 01.09.2017
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..