I would like to know if it is possible to add condition in the makeIcon() function
I have a table :
id ; lat ; long ; class1 ; class2 ; class3
And I want the icon to be different depending on these conditions :
if class1 == A, I want image1
else, if class2 == B, I want image2
else, if class3 == C, I want image 3
else, I want image4
Within makeIcon(), probably not. From the API documentation:
For the simple case of applying a single icon to a set of markers, use
makeIcon(). (link)
You probably want to use icon() which is a vector of icons that you can use to draw different icons for different data:
If you have several icons to apply that vary only by a couple of
parameters (i.e. they share the same size and anchor points but have
different URLs), use the icons() function.
Icons contains a vector with each icon image url (or any other icon property that differs among the data).
To do the logic, it might be easiest to nest an extra two ifelse statements into the icon function, something like:
iconUrl = ifelse(df$class1 == "a", "image1",
ifelse(df$class2 == "c", "image2",
ifelse(df$class3 =="x", "image3",
"some other url" #the else condition
)
)
),
Here's a minimal example that's just a slight addition to the example in the linked to api documentation:
library(leaflet)
lat<- c(57,65,60,61)
long<-c(-130,-125,-140,-135)
class1<-c("a","b","c","d")
class2<-c("b","c","d","e")
class3<-c("b","c","d","f")
df <- data.frame(lat,long,class1,class2,class3,stringsAsFactors=FALSE)
leafIcons <- icons(
iconUrl = ifelse(df$class1 == "a", "http://leafletjs.com/examples/custom-icons/leaf-green.png",
ifelse(df$class2 == "c", "http://leafletjs.com/examples/custom-icons/leaf-red.png",
ifelse(df$class3 == "d", "http://leafletjs.com/examples/custom-icons/leaf-orange.png",
"http://leafletjs.com/docs/images/logo.png"
)
)
),
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94,
shadowUrl = "http://leafletjs.com/examples/custom-icons/leaf-shadow.png",
shadowWidth = 50, shadowHeight = 64,
shadowAnchorX = 4, shadowAnchorY = 62
)
leaflet(data = df) %>% addTiles() %>%
addMarkers(~long, ~lat, icon = leafIcons)
Sorry the image selection in this isn't fantastic. If you want to some other property vary depending on the data for each icon, icon size for example, you use the same process on iconWidth and/or iconHeight:
iconHeight = ifelse(df$class1 == "a", 100,
ifelse(df$class2 == "c", 200,
ifelse(df$class3 == "d", 300,
400
)
)
),
Related
I am making a gt table showing the progress of individuals towards a goal. In the table, there is a row showing a horizontal bar graph of progress towards that goal (if goal is 50 and score is 40, the bar is at 80%).
However, when I change the order of the gt rows by using the groupname_col argument, the order of the other cells changes, but not the order of the gtExtras gt_plt_bar_pct column, so it's showing the wrong bars for the name and score in that row, instead, that column seems to always be represented in the order of rows in the input data.
I understand that I can fix this by using arrange on the df before the gt begins, but this doesn't seem like a good solution since I'm going to want to change the order of the rows to view by different groups. Is this a flaw with gtExtras? is there a better fix?
thanks!
reprex:
library(tibble)
library(gt)
library(gtExtras)
library(dplyr)
# make dataframe of individuals and their goals
df <- tribble(
~name, ~group, ~score, ~goal,
"Bob", "C", 20, 40,
"Chris", "A", 50, 40,
"Dale", "B", 30, 50,
"Jay", "A", 0, 40,
"Ben", "B", 10, 20
) %>%
# calculate percent towards goal, and cap at 100%
mutate(percent_to_goal = score/goal *100,
percent_to_goal = case_when(percent_to_goal >= 100 ~ 100,
TRUE ~ percent_to_goal))
df %>%
# this fixes the issue, but doesn't seem like a permanent solution
#arrange(group, name) %>%
# make gt table
gt(rowname_col = "name", groupname_col = "group") %>%
# order groups
row_group_order(groups = c("A","B","C")) %>%
# add bar chart column
gt_plt_bar_pct(column = percent_to_goal) %>%
# highlight blue if person reaches their goal
tab_style(
style = list(
cell_fill(color = "lightcyan"),
cell_text(weight = "bold")),
locations = cells_body(
columns = c(goal,score, percent_to_goal),
rows = score >= goal
)
)
Here is the output from the above code: notice that the length of the bar charts do not always reflect the values of the rows they are appearing in. Instead, they reflect the order of the original dataset.
EDIT: remove row_group_order. If I run the above code again, but comment out the line meant to rearrange the appearance of groups, the grouping shows up in a different order (order of appearance of groups in the original dataset), and the name and first two columns sort into these groups accordingly, but the bar chart column still does not, and remains in the original order of the dataset. Image below:
Per gtExtras v 0.2.4 this bug has been fixed. Thanks for raising and the great reprex!
library(tibble)
library(gt)
library(gtExtras)
library(dplyr)
# make dataframe of individuals and their goals
df <- tribble(
~name, ~group, ~score, ~goal,
"Bob", "C", 20, 40,
"Chris", "A", 50, 40,
"Dale", "B", 30, 50,
"Jay", "A", 0, 40,
"Ben", "B", 10, 20
) %>%
# calculate percent towards goal, and cap at 100%
mutate(percent_to_goal = score/goal *100,
percent_to_goal = case_when(percent_to_goal >= 100 ~ 100,
TRUE ~ percent_to_goal))
df %>%
# make gt table
gt(rowname_col = "name", groupname_col = "group") %>%
# order groups
row_group_order(groups = c("A","B","C")) %>%
# add bar chart column
gt_plt_bar_pct(column = percent_to_goal) %>%
# highlight blue if person reaches their goal
tab_style(
style = list(
cell_fill(color = "lightcyan"),
cell_text(weight = "bold")),
locations = cells_body(
columns = c(goal,score, percent_to_goal),
rows = score >= goal
)
)
I am building a large map where a number of observed variables map onto latent variables. When using DiagrammeR to generate the map, all the observed items (indicated by squares) appear in a single row. I was wondering if there was a way to sort them into multiple rows, say 5 or 10 in a row? The example below is based on actual data where there are 30+ items that map to one latent variable. The final map will have multiple sets of these, but right now, I am working on getting one to display correctly:
library(tidyverse)
library(DiagrammeR)
nodes <- create_node_df(
n=33,
nodes = 33,
label = make.unique(rep(letters, length.out = 33), sep=''),
tooltip = make.unique(rep(letters, length.out = 33), sep=''),
fontsize= 7,
shape = c("ellipse", "ellipse", rep("square", 31)),
fillcolor = "white",
width = 1,
height = 1,
)
# lines
edges <- create_edge_df(
from=c(1,rep(2,32)),
to = c(2,3,seq(3, 33, 1)),
)
create_graph(nodes_df = nodes,
edges_df = edges,
) %>%
render_graph(layout="tree")
This renders as:
However, what I am going for is more like:
Background
I was trying the create a Sankey graph like the following figure. Actually, I wanted to get a output where values (10, 20, 30, 40) will be set in the paths (from one node to another node).
How Did I Try?
At first, I tried using the Plotly library of Python. However, somewhere I have seen that it is not possible to set the values in the links or the paths of Sankey graph in Plotly (of Python). Later, I switched to R (for some other reasons also) where more resources are available (I think). However, here, I am also facing the same problem. I have checked many tutorials (e.g., this one), Q&A (e.g., 1, 2, 3) of SO which are in R. Still, I could not to find a tutorial or resources where the values are displayed in the paths!
My Question
How can I display the values on the links/paths of Sankey Graph, in R?
Note: This and this questions of SO seems to be similar. However, I failed to understand the way to incorporate those in my codes.
Example Code (collected from here)
# install.packages('networkD3')
library(networkD3)
nodes = data.frame("name" =
c("Node A", # Node 0
"Node B", # Node 1
"Node C", # Node 2
"Node D"))# Node 3
links = as.data.frame(matrix(c(
0, 1, 10, # Each row represents a link. The first number
0, 2, 20, # represents the node being conntected from.
1, 3, 30, # the second number represents the node connected to.
2, 3, 40),# The third number is the value of the node
byrow = TRUE, ncol = 3))
names(links) = c("source", "target", "value")
sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "name",
fontSize= 50, nodeWidth = 30)
This can be achieved by injecting custom JavaScript code when it's rendered using htmlwidgets::onRender(). The example below will initially position the link labels appropriately, but if the nodes are manually moved, the link labels will not automatically update accordingly. To achieve that, you would probably have to also override the default dragmove behaviour.
library(htmlwidgets)
library(networkD3)
nodes <-
data.frame(
name = c("Node A", "Node B", "Node C", "Node D")
)
links <-
data.frame(
source = c(0, 0, 1, 2),
target = c(1, 2, 3, 3),
value = c(10, 20, 30, 40)
)
p <- sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "name",
fontSize= 20, nodeWidth = 30)
htmlwidgets::onRender(p, '
function(el) {
var nodeWidth = this.sankey.nodeWidth();
var links = this.sankey.links();
links.forEach((d, i) => {
var startX = d.source.x + nodeWidth;
var endX = d.target.x;
var startY = d.source.y + d.sy + d.dy / 2;
var endY = d.target.y + d.ty + d.dy / 2;
d3.select(el).select("svg g")
.append("text")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("x", startX + ((endX - startX) / 2))
.attr("y", startY + ((endY - startY) / 2))
.text(d.value);
})
}
')
I'm developing an R shiny app and ideally I would need to do precisely what is done here:
More specifically, I have dataframe with stocks open, close, high, low data and I would need to replicate what's displayed in the attached image in column "Range".
I understand I should attach some code, but the truth here, I can't find anything close to what I'm asking online.
A sample dataframe would be:
df = data.frame(STOCK=c("IBM","MSFT","FB"), OPEN=c(100,90, 80), CLOSE=c(102, 85, 82), LOW=c(99,81,78), HIGH=c(105, 91, 88))
Then, I have no idea of what to do from here. Any suggestions? Thanks
You can use custom-rendering follow this guide
https://glin.github.io/reactable/articles/examples.html#custom-rendering-1
library(dplyr)
library(sparkline)
data <- chickwts %>%
group_by(feed) %>%
summarise(weight = list(weight)) %>%
mutate(boxplot = NA, sparkline = NA)
reactable(data, columns = list(
weight = colDef(cell = function(values) {
sparkline(values, type = "bar", chartRangeMin = 0, chartRangeMax = max(chickwts$weight))
}),
boxplot = colDef(cell = function(value, index) {
sparkline(data$weight[[index]], type = "box")
}),
sparkline = colDef(cell = function(value, index) {
sparkline(data$weight[[index]])
})
))
My Shiny app takes a dataframe like this:
and subsets appropriately by allowing the user to select a person (P1_name) and a date (date).
When initally launched, it looks like this:
and already, it is clear that the app isn't working. There should be a letter 'N' at the location of the town of Apple Valley, but instead there is nothing. I can't figure out why, since the DF has been subsetted correctly:
and the layers should be plotted correctly:
m <- leaflet(DF) %>%
addTiles() %>% # Add default OpenStreetMap map tiles
setView(lat=setzoom[1], lng=setzoom[2], zoom=zoom_num) %>%
addMarkers(lat=subset(DF, P1_outcome=='W')$lat, lng=subset(DF, P1_outcome=='W')$lon, icon = icon_W) %>%
addMarkers(lat=subset(DF, P1_outcome=='L')$lat, lng=subset(DF, P1_outcome=='L')$lon, icon = icon_L) %>%
addMarkers(lat=subset(DF, P1_outcome=='D')$lat, lng=subset(DF, P1_outcome=='D')$lon, icon = icon_D) %>%
addMarkers(lat=subset(DF, P1_outcome=='N')$lat, lng=subset(DF, P1_outcome=='N')$lon, icon = icon_N)
Unfortunately, this is just one symptom of some sort of skitzophrenic behavior that my app is displaying. If that was the only problem, I'd be rejoicing. Instead, say I select John Doe, on his first row (which should be Crecent City)
and BOOM I get:
How in the world did Leaflet think I had given it two sets of coordinates to plot, and what made it think that John Doe was drowing somewhere in the Pacific Ocean.
Nothing here makes much sense. I can't see a pattern in the chaos it is outputting. It's barely 100 lines of simple code.
Some ideas:
the conditionalPanel is mixing up my dataframe? I don't think so, since I can View(DF) and see that this part isn't the problem.
the layering in the icons isn't working? Not sure how this would be a problem, as we know that this is the correct way to plot icons.
I am getting an xtable warning, Warning in run(timeoutMs) : data length exceeds size of matrix, but this is just for the tableOutput part, which I don't think is related to any of the issue I'm beseiged with.
I'm stumped. Been stuck on this all day. If anyone has any insight, ideas, incantations, etc, I'd love to hear them.
UI.R
library(shiny)
library(ggplot2)
library(dplyr)
library(leaflet)
library(data.table)
options(xtable.include.rownames=F)
library(ggmap)
library(lubridate)
DF <- data.frame(lon=c(-120.6596156, -87.27751, -119.7725868, -124.2026, -117.1858759),
lat=c(35.2827524, 33.83122, 36.7468422, 41.75575, 34.5008311),
date=c('2014-03-14', '2014-01-11', '2013-11-22', '2012-08-23', '2013-08-23'),
location=c('San Luis Obispo', 'Jasper', 'Fresno', 'Crescent City', 'Apple Valley'),
P1_name=c('John Doe', 'John Doe', 'John Doe', 'John Doe', 'Joe Blow'),
P1_outcome=c('W', 'L', 'D', 'W', 'N'))
DF$date <- as.Date(DF$date, format="%Y-%m-%d")
DF <- arrange(DF, P1_name, date)
DT <- data.table(DF)
DT[, .date := sequence(.N), by = "P1_name"]
DF$date <- paste(DF$date, ' (', DT$.date, ')', sep='')
DF <- arrange(DF, P1_name, desc(date))
DF$P1_name <- as.character(DF$P1_name)
DF$P1_outcome <- as.character(DF$P1_outcome)
DF$location <- as.character(DF$P1_location)
#str(DF$P1_outcome)
icon_W <- makeIcon(
iconUrl = "http://i58.tinypic.com/119m3r5_th.gif",
iconWidth = 10, iconHeight = 23,
iconAnchorX = 10, iconAnchorY =23
)
icon_L <- makeIcon(
iconUrl = "http://i62.tinypic.com/2dulcvq_th.jpg",
iconWidth = 10, iconHeight = 23,
iconAnchorX = 10, iconAnchorY = 23
)
icon_D <- makeIcon(
iconUrl = "http://i58.tinypic.com/2zox2yf_th.gif",
iconWidth = 10, iconHeight = 23,
iconAnchorX = 10, iconAnchorY = 23
)
icon_N <- makeIcon(
iconUrl = "http://i62.tinypic.com/339j7de_th.gif",
iconWidth = 10, iconHeight = 23,
iconAnchorX = 22, iconAnchorY = 94
)
server <- function(input, output, session) {
output$dates<-renderUI({
selectInput('dates', 'by date / number', choices=DF[which(DF$P1_name == input$person), ]$date, selectize = FALSE)
})
output$map<-renderLeaflet({
validate(
need(!is.null(input$dates),""),
need(!is.null(input$person),"")
)
if(input$radio=='by date'){
DF <- filter(DF, P1_name==input$person, date==input$dates)
View(DF)
zoom_num <- 5
setzoom <- c(DF$lat, DF$lon)
outcome <- data.frame(DF$P1_outcome, DF$location)
output$table <- renderTable(outcome)
}
else{
DF <- filter(DF, P1_name==input$person)
View(DF)
zoom_num <- 2
setzoom <- c(DF$lat[1], DF$lon[1])
outcome <- data.frame(DF$P1_outcome, DF$location)
output$table <- renderTable(outcome)
}
m <- leaflet(DF) %>%
addTiles() %>% # Add default OpenStreetMap map tiles
setView(lat=setzoom[1], lng=setzoom[2], zoom=zoom_num) %>%
addMarkers(lat=subset(DF, P1_outcome=='W')$lat, lng=subset(DF, P1_outcome=='W')$lon, icon = icon_W) %>%
addMarkers(lat=subset(DF, P1_outcome=='L')$lat, lng=subset(DF, P1_outcome=='L')$lon, icon = icon_L) %>%
addMarkers(lat=subset(DF, P1_outcome=='D')$lat, lng=subset(DF, P1_outcome=='D')$lon, icon = icon_D) %>%
addMarkers(lat=subset(DF, P1_outcome=='N')$lat, lng=subset(DF, P1_outcome=='N')$lon, icon = icon_N)
}) #<- end output$map
} #<- end server function
ui <- fluidPage(
titlePanel("Location Explorer"),
sidebarLayout (
sidebarPanel(
selectInput('person', 'Select person', choices=unique(DF$P1_name), selectize = FALSE),
radioButtons('radio', 'Select row(s)', choices=c('by date', 'all'), selected = NULL, inline = TRUE),
conditionalPanel(
condition = "input.radio == 'by date'",
uiOutput('dates')
),
conditionalPanel(
condition = "input.radio == 'all'"
)
),
mainPanel(
leafletOutput('map'),
fluidRow(column(4, tableOutput('table')))
))
) #<- end ui
shinyApp(ui = ui, server = server)
One of the issue could be that you are adding empty markers in your subsets and leaflet reacts strangely to that.
For example, when you select Joe Blow, all the subsets for P1_outcome == "W", "L" or "D" are empty.
As described here, you could use the iconList function to change the icons depending on P1_outcome and remove all the subset.
You could for example add:
icon_list <- iconList(W=icon_W,L=icon_L,D=icon_D,N=icon_N)
right after you define all the icons, and use:
m <- leaflet(DF) %>%
addTiles() %>% # Add default OpenStreetMap map tiles
setView(lat=setzoom[1], lng=setzoom[2], zoom=zoom_num) %>%
addMarkers(lat=DF$lat, lng=DF$lon,icon= ~icon_list[DF$P1_outcome])
to create your map.