I’m trying to display a table and its plot side by side in a page.
The goal is that the plot’s height matches with the table’s one !
I tried with st.container and st.columns combined but it does not work (see image below).
import streamlit as st
import plotly.express as px
long_df = px.data.medals_long()
fig = px.bar(long_df, x="nation", y="count",
color="medal", title="Long-Form Input")
data_container = st.container()
with data_container:
table, plot = st.columns(2)
with table:
st.table(long_df)
with plot:
st.plotly_chart(fig, use_container_width=True)
>>> Output (in the web browser):
As you can see, the plot is much longer than the table.
Do you know how to fix this, please ?
Important detail : I'm using different tables, so the number of rows is changing constantly.
Unfortunately, neither st.columns() nor st.container has a height parameter. You can achieve this using the height parameters of the plotly figure and st.dataframe(). When we define height for the dataframe, the user can scroll down to see whole table.
height = 400 # px
fig = px.bar(
long_df, x="nation", y="count",
color="medal", title="Long-Form Input",
height=height,
)
# update margin to show title on top
fig.update_layout(margin={"t": 30, "b": 0})
data_container = st.container()
with data_container:
table, plot = st.columns(2)
with table:
# use st.dataframe instead of st.table
st.dataframe(long_df, height=height)
with plot:
st.plotly_chart(fig, use_container_width=True)
Another way of visualising data along with the table is to use st.tabs().
tab1, tab2 = st.tabs(["Data", "Chart"])
with tab1:
st.table(long_df)
with tab2:
st.plotly_chart(fig, use_container_width=True)
I'm failing to get left aligned numbers in kableExtra in combination with formattable's barplots in table cells.
Code to reproduce in rmarkdown format:
title: "Untitled"
output: html_document
knitr::opts_chunk$set(echo = TRUE)
library("formattable")
library("kableExtra")
Example
summary(cars)
cars$speed <- cars$speed * 100
Get a nice table
The table's first column is filled with tiny bar plots.
All code is written following http://haozhu233.github.io/kableExtra/awesome_table_in_html.html manual of kableExtra.
plot.table <- cars
# need to modify the first few values to make sure the effect is visible.
plot.table$speed[1] <- 3
plot.table$speed[2] <- 25
plot.table$speed[3] <- 100
plot.table$speed <- color_bar("lightgreen")(plot.table$speed)
kbl(x = plot.table,
escape = F) %>%
kable_styling(bootstrap_options = c("striped", "hover"), fixed_thead = T)
The first three rows show values that are small in comparison to the rest of the values in the table.
This makes theirs bars short and results in weirdly aligned numbers. The alignment is done according to the rightmost end of the green bar.
Inspecting the respective html element (e.g. FireFox "Inspect Element") reveals that the element "direction" is "rtl", which seems to be the default in kableExtra.
Manually modifying the value of this parameter to "ltr" yields nicely left aligned values extending across the bar's end.
Unfortunately, I couldn't find the parameter in color_bar to modify this behaviour.
Any help pointing me to a way to get the values left aligned would be highly appreciated.
as #juljo mentioned one obviously needs to modify the respective function. The body of the function could be copied out of the package contents.
f.color_bar <- function (color = "lightgray", fun = "proportion", ...)
{
fun <- match.fun(fun)
formattable::formatter("span", style = function(x) style(display = "inline-block",
direction = "ltr", `border-radius` = "4px",
`padding-right` = "2px", `background-color` = csscolor(color),
width = percent(fun(as.numeric(x), ...))))
}
This hard coded the left to right behaviour, but isn't variable here as well.
I'm producing a plot_gene_map figure by the genoPlotR R package, which gives a horizontal phylogenetic tree where aligned with each leaf is a genomic segment.
Here's a simple example that illustrates my usage and problem:
The plot_gene_map function requires an ade4s' package phylog object which represents the phylogenetic tree:
tree <- ade4::newick2phylog("(((A:0.08,B:0.075):0.028,(C:0.06,D:0.06):0.05):0.0055,E:0.1);")
A list of genoPlotR's dna_seg objects (which are essentially data.frames with specific columns), where the names of the list elements have to match the names of the leaves of tree:
dna.segs.list <- list(A=genoPlotR::as.dna_seg(data.frame(name=paste0("VERY.LONG.NAME.A.",1:10),start=seq(1,91,10),end=seq(5,95,10),strand=1,col="black",ly=1,lwd=1,pch=1,cex=1,gene_type="blocks",fill="red")),
B=genoPlotR::as.dna_seg(data.frame(name=paste0("VERY.LONG.NAME.B.",1:10),start=seq(1,91,10),end=seq(5,95,10),strand=1,col="black",ly=1,lwd=1,pch=1,cex=1,gene_type="blocks",fill="blue")),
C=genoPlotR::as.dna_seg(data.frame(name=paste0("VERY.LONG.NAME.C.",1:10),start=seq(1,91,10),end=seq(5,95,10),strand=1,col="black",ly=1,lwd=1,pch=1,cex=1,gene_type="blocks",fill="green")),
D=genoPlotR::as.dna_seg(data.frame(name=paste0("VERY.LONG.NAME.D.",1:10),start=seq(1,91,10),end=seq(5,95,10),strand=1,col="black",ly=1,lwd=1,pch=1,cex=1,gene_type="blocks",fill="yellow")),
E=genoPlotR::as.dna_seg(data.frame(name=paste0("VERY.LONG.NAME.E.",1:10),start=seq(1,91,10),end=seq(5,95,10),strand=1,col="black",ly=1,lwd=1,pch=1,cex=1,gene_type="blocks",fill="orange")))
And a list of genoPlotR's annotation objects, which give coordinate information, also named according to the tree leaves:
annotation.list <- lapply(1:5,function(s){
mids <- genoPlotR::middle(dna.segs.list[[s]])
return(genoPlotR::annotation(x1=mids,x2=NA,text=dna.segs.list[[s]]$name,rot=30,col="black"))
})
names(annotation.list) <- names(dna.segs.list)
And the call to the function is:
genoPlotR::plot_gene_map(dna_segs=dna.segs.list,tree=tree,tree_width=2,annotations=annotation.list,annotation_height=1.3,annotation_cex=0.9,scale=F,dna_seg_scale=F)
Which gives:
As you can see the top and right box (gene) names get cut off.
I tried playing with pdf's width and height, when saving the figure to a file, and with the margins through par's mar, but they have no effect.
Any idea how to display this plot without getting the names cut off?
Currently genoPlotR's plot_gene_map does not have a legend option implemented. Any idea how can I add a legend, let's say which shows these colors in squares aside these labels:
data.frame(label = c("A","B","C","D","E"), color = c("red","blue","green","yellow","orange"))
Glad that you like genoPlotR.
There are no real elegant solution to your problem, but here are a few things you can attempt:
- increase annotation_height and reduce annotation_cex
- increase rotation (“rot”) in the annotation function
- use xlims to artificially increase the length of the dna_seg (but that’s a bad hack)
For the rest (including the legend), you’ll have to use grid and its viewports.
A blend of the first 3 solutions:
annotation.list <- lapply(1:5,function(s){
mids <- genoPlotR::middle(dna.segs.list[[s]])
return(genoPlotR::annotation(x1=mids, x2=NA, text=dna.segs.list[[s]]$name,rot=75,col="black"))
})
names(annotation.list) <- names(dna.segs.list)
genoPlotR::plot_gene_map(dna_segs=dna.segs.list,tree=tree,tree_width=2,annotations=annotation.list,annotation_height=5,annotation_cex=0.4,scale=F,dna_seg_scale=F, xlims=rep(list(c(0,110)),5))
For the better solution with grid: (note the "plot_new=FALSE" in the call to plot_gene_map)
# changing rot to 30
annotation.list <- lapply(1:5,function(s){
mids <- genoPlotR::middle(dna.segs.list[[s]])
return(genoPlotR::annotation(x1=mids,x2=NA,text=dna.segs.list[[s]]$name,rot=30,col="black"))
})
names(annotation.list) <- names(dna.segs.list)
# main viewport: two columns, relative widths 1 and 0.3
pushViewport(viewport(layout=grid.layout(1,2, widths=unit(c(1, 0.3), rep("null", 2))), name="overall_vp"))
# viewport with gene_map
pushViewport(viewport(layout.pos.col=1, name="geneMap"))
genoPlotR::plot_gene_map(dna_segs=dna.segs.list,tree=tree,tree_width=2,annotations=annotation.list,annotation_height=3,annotation_cex=0.5,scale=F,dna_seg_scale=F, plot_new=FALSE)
upViewport()
# another viewport for the margin/legend
pushViewport(viewport(layout.pos.col=2, name="legend"))
plotLegend(…)
upViewport()
Hope that helps!
Lionel
Which function or package could I use to add the legend? The R base functions did not seem to work for me. The following message is displayed:
Error in strheight(legend, units = "user", cex = cex) :
plot.new has not been called yet"
I want to add space between plots but when I play with margin, it either overlaps or cuts.
Here is the code:
library(plotly)
plotList <- function(nplots) {
lapply(seq_len(nplots), function(x) plot_ly())
}
s1 <- subplot(plotList(6), nrows = 2, shareX = TRUE, shareY = TRUE)
s2 <- subplot(plotList(2), shareY = TRUE)
p <- subplot(s1, s2, plot_ly(), nrows = 3, margin = 0.04, heights = c(0.6, 0.3, 0.1))
print(p)
I obtain this:
Whereas i would rather like something like this (image done with paint) with more spacing between the different subplots:
How should I do ?
I was facing the same problem but, fortunately, I found a solution. You can use margin as an argument in Subplot function as follows:
subplot(plot1,
plot2,
nrows = 2,
margin = 0.07)
According to Plotly documentation, you can define only one or four values for each of the margins and those values should be between 0 and 1. If you provide only one value it will be used for all four margins or if you provide four values it will be used in the following order: the first one will be the left margin, the second one will be the right margin, the third one will be the top margin and the last one will be the bottom margin. You can play around and define the values that better fit the layout you want to create.
I found a solution by inserting blank plots between the normal plot. It is however a bit awkward but it works and I've found nothing better
blankplot<-plot_ly()%>%
layout(xaxis=list(visible="FALSE",color="white",tickfont =list(color="white")),
yaxis=list(visible="FALSE",color="white",tickfont =list(color="white")))
The gridExtra package adds a grob of class "pattern" that lets one fill rectangles with patterns. For example,
library(gridExtra)
grid.pattern(pattern = 1)
creates a box filled with diagonal lines. I want to create a stack of panels in which each panel is filled with these diagonal lines. This is easy:
library(lattice); library(gridExtra)
examplePlot <- xyplot(
1 ~ 1 | 1:2,
panel = function () grid.pattern(pattern = 1),
layout = c(1, 2),
# Remove distracting visual detail
scales = list(x=list(draw=FALSE), y=list(draw=FALSE)),
strip = FALSE, xlab = '', ylab = ''
)
print(examplePlot)
The problem is that the diagonal lines aren't aligned across panels. That is, there is a visual "break" where the bottom of the first panel meets the top of the second panel: at that point, the lines don't line up. This is the problem that I want to fix.
I can eliminate most of the visual break by adding the argument pattern.offset = c(.2005, 0) to the grid.pattern call, and making sure that it applies only to the bottom panel. But this solution doesn't generalize. For example, if I change the pattern (e.g., by using the granularity argument to grid.pattern), this solution won't work. Is there a more general fix?
To make this work, you'll have to take charge of setting the panel.height argument used by print.trellis. (To see why, try resizing your plotting device after running your example code: as the size of the device and the panels changes, so does the matching/mismatching of the lines):
## Calculate vertical distance (in mm) between 45 degree diagonal lines
## spaced 5mm apart (the default distance for grid.pattern).
vdist <- 5 * sqrt(2)
nLines <- 8L ## can be any integer
panelHeight <- list(x = nLines*vdist, units = "mm", data = NULL)
## Plot it
print(examplePlot, panel.height=panelHeight)