Как уменьшить размер вывода Rplotly html путем округления значений графика

Я пытаюсь уменьшить размер моего html-отчета RMarkdown и, что более важно, ускорить его открытие. Отчет в формате HTML состоит из большого количества графиков R Plotly, каждый из которых содержит большое количество точек данных (более 1000). Учитывая, что R Plotly хранит все необработанные данные для каждого графика в html-файле, я считаю, что хорошим вариантом уменьшения размера файла является округление десятичных знаков в данных. Однако я обнаружил, что даже несмотря на то, что входные данные были округлены, R Plotly по-прежнему поддерживает большое количество десятичных знаков в html-файле. Следовательно, размер файла не уменьшается при округлении данных.

Ниже приведены два случая: базовый вариант, содержащий необработанные данные, и случай округления, содержащий округленные данные. Размер файла одинаков в обоих случаях.

Базовый вариант HTML

RawData <- data.frame(Date = seq(as.Date("2024/1/1"), by = "month", length.out = 12),
                      PreciseValue = c(0.1516270, 0.3542629, 0.8339342, 0.5796813, 0.3933472, 0.2937137, 0.1779205, 0.4285533, 0.6841885, 0.3399411,0.99476560, 0.42941527))
RawData$RoundValue <- round(RawData$PreciseValue,2)
fig <- plot_ly(RawData, type = 'scatter', mode = 'lines')%>%
  add_trace(x = ~Date, y = ~PreciseValue, name = 'PreciseValue')
saveWidget(fig, "plotly_base.html", selfcontained = TRUE)

Размер HTML-файла составляет 3780 КБ. Если я открою html-файл и посмотрю на базовые данные R Plotly, сохраненные данные y будут следующими:

"y":[0.15162704353000001,0.35426295622999998,0.83393426323999997,0.57968136341999998,0.39334726234,0.29371352347000002,0.17792423404999999,0.44352285533000002,0.68418423485000002,0.36623994110000002,0.99476432455999997,0.42941523452699998]

Обратите внимание, что десятичных знаков больше, чем в исходных данных.

Случай округления значений

RawData$RoundValue <- round(RawData$PreciseValue,2)
fig <- plot_ly(RawData, type = 'scatter', mode = 'lines')%>%
  add_trace(x = ~Date, y = ~RoundValue, name = 'RoundValue')

saveWidget(fig, "plotly_round.html", selfcontained = TRUE)

Размер html-файла круглого корпуса также составляет 3780 КБ. Исходными данными для этого случая являются

"y":[0.14999999999999999,0.34999999999999998,0.82999999999999996,0.57999999999999996,0.39000000000000001,0.28999999999999998,0.17999999999999999,0.44,0.68000000000000005,0.37,0.98999999999999999,0.42999999999999999]

Сохраненные данные y должны быть чем-то вроде

"y":[0.15, 0.35, 0.83, 0.58, 0.39, 0.29, 0.18, 0.44, 0.68, 0.37, 0.99, 0.43]

Кто-нибудь знает, как настроить R Plotly для хранения только настроенного количества десятичных знаков в выводе html?

Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
3
0
98
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете сделать это

library(plotly)
library(htmlwidgets)

RawData <- data.frame(
  Date = seq(as.Date("2024/1/1"), by = "month", length.out = 12),
  PreciseValue = c(0.1516270, 0.3542629, 0.8339342, 0.5796813, 0.3933472, 0.2937137, 0.1779205, 0.4285533, 0.6841885, 0.3399411, 0.99476560, 0.42941527)
)

fig <- plot_ly(RawData, type = 'scatter', mode = 'lines') %>%
  add_trace(x = ~Date, y = ~PreciseValue, name = 'PreciseValue')

saveWidget(fig, "plotly_base.html", selfcontained = TRUE)

html_content <- readLines("plotly_base.html")
html_content <- gsub(
  pattern = "([0-9]+\\.[0-9]{2})[0-9]*", 
  replacement = "\\1", 
  x = html_content
)

writeLines(html_content, "plotly_rounded.html")

который дает

и в html у тебя будет

<script type = "application/json" data-for = "htmlwidget-f7b8f28dbb6776fbb0e5">{"x":{"visdat":{"60443a954e78":["function () ","plotlyVisDat"]},"cur_data":"60443a954e78","attrs":{"60443a954e78":{"mode":"lines","alpha_stroke":1,"sizes":[10,100],"spans":[1,20],"type":"scatter"},"60443a954e78.1":{"mode":"lines","alpha_stroke":1,"sizes":[10,100],"spans":[1,20],"type":"scatter","x":{},"y":["0.15","0.35","0.83","0.58","0.39","0.29","0.18","0.43","0.68","0.34","0.99","0.43"],"name":"RoundValue","inherit":true}},"layout":{"margin":{"b":40,"l":60,"t":25,"r":10},"xaxis":{"domain":[0,1],"automargin":true,"title":"Date"},"yaxis":{"domain":[0,1],"automargin":true,"title":[]},"hovermode":"closest","showlegend":true},"source":"A","config":{"modeBarButtonsToAdd":["hoverclosest","hovercompare"],"showSendToCloud":false},"data":[{"mode":"lines","type":"scatter","marker":{"color":"rgba(31,119,180,1)","line":{"color":"rgba(31,119,180,1)"}},"error_y":{"color":"rgba(31,119,180,1)"},"error_x":{"color":"rgba(31,119,180,1)"},"line":{"color":"rgba(31,119,180,1)"},"xaxis":"x","yaxis":"y","frame":null},{"mode":"lines","type":"scatter","x":["2024-01-01","2024-02-01","2024-03-01","2024-04-01","2024-05-01","2024-06-01","2024-07-01","2024-08-01","2024-09-01","2024-10-01","2024-11-01","2024-12-01"],"y":["0.15","0.35","0.83","0.58","0.39","0.29","0.18","0.43","0.68","0.34","0.99","0.43"],"name":"RoundValue","marker":{"color":"rgba(255,127,14,1)","line":{"color":"rgba(255,127,14,1)"}},"error_y":{"color":"rgba(255,127,14,1)"},"error_x":{"color":"rgba(255,127,14,1)"},"line":{"color":"rgba(255,127,14,1)"},"xaxis":"x","yaxis":"y","frame":null}],"highlight":{"on":"plotly_click","persistent":false,"dynamic":false,"selectize":false,"opacityDim":0.20000000000000001,"selected":{"opacity":1},"debounce":0},"shinyEvents":["plotly_hover","plotly_click","plotly_selected","plotly_relayout","plotly_brushed","plotly_brushing","plotly_clickannotation","plotly_doubleclick","plotly_deselect","plotly_afterplot","plotly_sunburstclick"],"base_url":"https://plot.ly"},"evals":[],"jsHooks":[]}</script>

Вычисление округленного значения приводит к ошибке (неожиданно [).

Anthony 20.05.2024 05:12

Виноват. Я случайно вставил ссылку на изображение в код. Отредактировал это.

Serge de Gosson de Varennes 20.05.2024 05:14

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

Anthony 20.05.2024 05:30

Я изменил свой код и добавил часть вывода HTML, содержащую округленные значения. Проверьте, этого ли вы ожидали.

Serge de Gosson de Varennes 20.05.2024 06:14

График риса не работает и выдает предупреждающее сообщение:Can't display both discrete & non-discrete data on same axis. Это происходит потому, что столбец RoundValue имеет символьный формат из-за функции sprintf.

Anthony 20.05.2024 06:19

Я изменил подход к этому. Это должно сработать и для вас.

Serge de Gosson de Varennes 20.05.2024 08:04

Когда вы создаете график с помощью data.frame,plotly сохраняет базовый data.frame в браузере, поэтому не имеет значения, какие столбцы вы используете для построения графика, если вы не удалите их из базового data.frame.

В примере ниже:

  • Для new plot — обратите внимание: если вы удалите столбец PreciseValue, он не перейдет на вашу фигуру.
  • Также учтите, что если вы передаете много других столбцов в свой реальный набор данных, вы можете удалить их.
library(dplyr)
library(plotly)


# your fig ----------------------------------------------------------------
RawData <- data.frame(Date = seq(as.Date("2024/1/1"), by = "month", length.out = 12),
                      PreciseValue = c(0.1516270, 0.3542629, 0.8339342, 0.5796813, 0.3933472, 0.2937137, 0.1779205, 0.4285533, 0.6841885, 0.3399411,0.99476560, 0.42941527))
RawData$RoundValue <- round(RawData$PreciseValue,2)
fig <- plot_ly(RawData, type = 'scatter', mode = 'lines')%>%
  add_trace(x = ~Date, y = ~PreciseValue, name = 'PreciseValue')

plotly::plotly_data(fig)
#> # A tibble: 12 × 3
#>    Date       PreciseValue RoundValue
#>    <date>            <dbl>      <dbl>
#>  1 2024-01-01        0.152       0.15
#>  2 2024-02-01        0.354       0.35
#>  3 2024-03-01        0.834       0.83
#>  4 2024-04-01        0.580       0.58
#>  5 2024-05-01        0.393       0.39
#>  6 2024-06-01        0.294       0.29
#>  7 2024-07-01        0.178       0.18
#>  8 2024-08-01        0.429       0.43
#>  9 2024-09-01        0.684       0.68
#> 10 2024-10-01        0.340       0.34
#> 11 2024-11-01        0.995       0.99
#> 12 2024-12-01        0.429       0.43


# new fig -----------------------------------------------------------------
RawData <- data.frame(
  Date = seq(as.Date("2024/1/1"), by = "month", length.out = 12),
  PreciseValue = c(0.1516270, 0.3542629, 0.8339342, 0.5796813, 0.3933472, 0.2937137, 0.1779205, 0.4285533, 0.6841885, 0.3399411,0.99476560, 0.42941527)
  ) %>% 
  mutate(
    RoundValue = round(PreciseValue, 2)
  ) %>% 
  select(
    Date, RoundValue
  )


fig2 <- plot_ly(RawData, type = 'scatter', mode = 'lines')%>%
  add_trace(x = ~Date, y = ~RoundValue, name = 'RoundValue')


plotly::plotly_data(fig2)
#> # A tibble: 12 × 2
#>    Date       RoundValue
#>    <date>          <dbl>
#>  1 2024-01-01       0.15
#>  2 2024-02-01       0.35
#>  3 2024-03-01       0.83
#>  4 2024-04-01       0.58
#>  5 2024-05-01       0.39
#>  6 2024-06-01       0.29
#>  7 2024-07-01       0.18
#>  8 2024-08-01       0.43
#>  9 2024-09-01       0.68
#> 10 2024-10-01       0.34
#> 11 2024-11-01       0.99
#> 12 2024-12-01       0.43

Created on 2024-05-20 with reprex v2.1.0

Я попробовал этот подход, но когда я сохранил fig2 с помощью saveWidget, размер html-файла был таким же, а количество десятичных знаков в файлах все еще было чрезмерно большим.

Anthony 20.05.2024 21:23
Ответ принят как подходящий

plotly использует методы htmlwidgets для сохранения своих данных. Частично это связано с тем, что отображаемый объект содержит функцию для сохранения своих данных в формате JSON. В вашем примере используется функция

> attr(fig$x, "TOJSON_FUNC")
function (x, ...) 
{
    jsonlite::toJSON(x, digits = 50, auto_unbox = TRUE, force = TRUE, 
        null = "null", na = "null", time_format = "%Y-%m-%d %H:%M:%OS6", 
        ...)
}
<bytecode: 0x12084ae50>
<environment: namespace:plotly>

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

attr(fig$x, "TOJSON_FUNC") <- function (x, ...) 
{
  jsonlite::toJSON(x, digits = 2, auto_unbox = TRUE, force = TRUE, 
                   null = "null", na = "null", time_format = "%Y-%m-%d %H:%M:%OS6", 
                   ...)
}

saveWidget(fig, "plotly_base.html", selfcontained = TRUE)

Когда я это делаю, данные сохраняются всего с двумя знаками после запятой.

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

Это выглядит как отличный вариант для уменьшения количества хранимых десятичных знаков. Как мне внести изменения? Я предполагаю, что мне нужно отредактировать вызов Plotly к функции toJSON. Нужно ли мне изменять исходный код Plotly?

Anthony 20.05.2024 20:06

@Энтони: Нет, код, который я опубликовал, — это все, что вам нужно сделать. Вычислите фигуру обычным способом, затем измените ее атрибут TOJSON_FUNC и сохраните ее обычным способом.

user2554330 21.05.2024 11:17

Я не думаю, что digits=2 здесь (или в других ответах) правильно делать, потому что он усекает небольшие значения, например, график со значениями x в диапазоне .001-.009 будет иметь только значения 0 и .01. . Было бы лучше выяснить, сколько цифр требуется, чтобы график оставался значимым, например, вычислив с помощью for digits= результат чего-то вроде min(0, -log10(diff(range(x))) + 2L). Это заставляет задуматься, почему автор сюжета не делает этого, возможно, потому, что потеря точности (часто) важна, например, при отображении в высоком разрешении.

Martin Morgan 21.05.2024 15:50

@MartinMorgan: Если вы используете digits = I(2), вместо signif () будет использоваться round(), поэтому вы всегда будете получать две значащие цифры. И если у вас есть более сложный пример, где некоторые столбцы требуют другой обработки, чем другие, это также возможно с помощью этого подхода, хотя это требует больше работы. В функции TOJSON_FUNC вы сначала округляете каждый столбец, а затем вызываете jsonlite::toJSON() с помощью digits = NA.

user2554330 21.05.2024 16:32

Это очень хорошо сработало для моей проблемы. Я также мог бы изменить формат даты и времени, чтобы удалить ненужные 000. Я использовал решение своей более крупной проблемы, и оно уменьшило размер html-файла с 47 МБ до 42 МБ. Спасибо пользователю 2554330.

Anthony 22.05.2024 21:27

Другой вариант с xml2 для непосредственного изменения сгенерированного .html.

Исходные данные: Глава 2. Интерактивные графики. Визуализация данных с помощью R.

Входные данные (данные первого графика):

[1] "{\"x\":{\"data\":[{\"x\":[0.0328321225432224,0.109320252277505,0.293389955686854,0.25579692457896,0.039311241065627,0.146064301552106,0.181909295915811,0.311815561959654,0.317128096436614,0.0955315870570108,0.136271785804496,0.0546152503926016,0.0613091709712931,0.0890071477017791,0.234544596507787,0.171584073331424,0.0137459607258265,0.0647079899677535,0.494673295454545,0.472161572052402,0.0830819851933096,0.1285972177976,0.123681687440077,0.0317001620035903,0.0190664607303797,0.191960252935863,0.212282255683496,0.546967895362664,0.211626387981711,0.0487558624283481,0.124644280022766,0.547699386503067,0.0800054704595186,0.0850793972908528,0.209757716561567,0.037748128864302,0.109842719431761,0.0941492343551766,0.0636989670437777,0.174081035923141,0.0699268354217475,0.0638297872340425,0.132262282832057,0.0656909202078875,0.0357040323633042,0.0145872865275142,0.00785264738148854,0.511931766627348,0.340971780182474,0.00423536530025715,0.184762773722628,0.00249500998003992,0.104696511947267,0.0480663303762973,0.0370172390752372,0.0981524249422633,0.0967281719611946,0.0999784063917081,0.0915903413821815,0.152237710931768,0.0062984496124031,0.21302783563719,0.244618964514252,0.171130648129217,0.0188772975658222,0.272753623188406,0.152310409104287,0.0394015701377574,0.10632216760032,0.0971946727118164,0.183070866141732,0.000505433409148345,0.143502257902659,0.139184397163121,0.0550271444082519,0.0418168914123492,0.193761696818465,0.030040733197556,0.00514917461759806,0.252943155062227,0.0354451008352007,0.136290021178522],

Код для округления каждого элемента данных графика до 2 цифр:

###Packages
library(rvest)
library(xml2)
library(dplyr)
library(purrr)
library(jsonlite)

### Parse the generated HTML file
src=read_html("c://Users/your_name/Downloads/markdown_with_plotly.htm")
### Locate the script elements which contain the data
ext=src %>% html_elements(xpath = "//script[@data-for]")

### Write a function to round the data of plotly graphs
### First, extract the json data and pull out the dataframe containing the numbers
### Then, apply a function to specific columns (here x & y) to round data
### Overwrite the original dataframe with the new one
### Convert back the parsed JSON (list) to JSON (character)
### Extract the attributes of the script element
### Create a new sibling node (script) with the JSON object previously created
### Set the attributes of the new script node with the attributes previously extracted
### To finish remove the original script node

babar=function(x){json=fromJSON(html_text2(x))
df=json[["x"]][["data"]]
fix=df %>% mutate(across(c(x,y),~map(.x,function(x){round(x,digits = 2)})))
json$x$data<-fix
new=toJSON(json)
attrs=x %>% 
  html_attrs() %>% 
  unlist()
xml_set_attrs(xml_add_sibling(x, .value = "script", new),value = attrs)
xml_remove(x)
}

### We apply the funtion to each script elements
walk(ext,babar,.progress = TRUE)

### Write a new HTML file
write_html(src,"C://Users/your_name/Downloads/plotly.html")

Вывод (данные для первого графика):

[1] "{\"x\":{\"data\":[{\"x\":[0.03,0.11,0.29,0.26,0.04,0.15,0.18,0.31,0.32,0.1,0.14,0.05,0.06,0.09,0.23,0.17,0.01,0.06,0.49,0.47,0.08,0.13,0.12,0.03,0.02,0.19,0.21,0.55,0.21,0.05,0.12,0.55,0.08,0.09,0.21,0.04,0.11,0.09,0.06,0.17,0.07,0.06,0.13,0.07,0.04,0.01,0.01,0.51,0.34,0,0.18,0,0.1,0.05,0.04,0.1,0.1,0.1,0.09,0.15,0.01,0.21,0.24,0.17,0.02,0.27,0.15,0.04,0.11,0.1,0.18,0,0.14,0.14,0.06,0.04,0.19,0.03,0.01,0.25,0.04,0.14],

Этот подход немного сложнее, чем подход user2554330.

Anthony 22.05.2024 21:30

Другие вопросы по теме