Я пытаюсь уменьшить размер моего 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?
Вы можете сделать это
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>
Виноват. Я случайно вставил ссылку на изображение в код. Отредактировал это.
Я не вижу, чем это отличается от моего случая округления значений. Выглядит точно так же? HTML-файл по-прежнему будет содержать дополнительные десятичные знаки.
Я изменил свой код и добавил часть вывода HTML, содержащую округленные значения. Проверьте, этого ли вы ожидали.
График риса не работает и выдает предупреждающее сообщение:Can't display both discrete & non-discrete data on same axis
. Это происходит потому, что столбец RoundValue имеет символьный формат из-за функции sprintf.
Я изменил подход к этому. Это должно сработать и для вас.
Когда вы создаете график с помощью 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-файла был таким же, а количество десятичных знаков в файлах все еще было чрезмерно большим.
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?
@Энтони: Нет, код, который я опубликовал, — это все, что вам нужно сделать. Вычислите фигуру обычным способом, затем измените ее атрибут TOJSON_FUNC
и сохраните ее обычным способом.
Я не думаю, что digits=2
здесь (или в других ответах) правильно делать, потому что он усекает небольшие значения, например, график со значениями x в диапазоне .001-.009 будет иметь только значения 0 и .01. . Было бы лучше выяснить, сколько цифр требуется, чтобы график оставался значимым, например, вычислив с помощью for digits=
результат чего-то вроде min(0, -log10(diff(range(x))) + 2L)
. Это заставляет задуматься, почему автор сюжета не делает этого, возможно, потому, что потеря точности (часто) важна, например, при отображении в высоком разрешении.
@MartinMorgan: Если вы используете digits = I(2)
, вместо signif ()
будет использоваться round()
, поэтому вы всегда будете получать две значащие цифры. И если у вас есть более сложный пример, где некоторые столбцы требуют другой обработки, чем другие, это также возможно с помощью этого подхода, хотя это требует больше работы. В функции TOJSON_FUNC
вы сначала округляете каждый столбец, а затем вызываете jsonlite::toJSON()
с помощью digits = NA
.
Это очень хорошо сработало для моей проблемы. Я также мог бы изменить формат даты и времени, чтобы удалить ненужные 000. Я использовал решение своей более крупной проблемы, и оно уменьшило размер html-файла с 47 МБ до 42 МБ. Спасибо пользователю 2554330.
Другой вариант с 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.
Вычисление округленного значения приводит к ошибке (неожиданно
[
).