I was wondering how to set the width of an rChart using % as opposed to px. I noticed in the source code that it defaults to pixels. I'm trying to use it in a shiny app, and fixed with charts seem to be an issue as they don't scale with the rest of the user interface. Is there a way around this?
This should be considered a hack, since neither rCharts or most of the libraries have built in responsive behavior. However, one change would be to define your own renderChart function with this behavior. Something like this might work if you define in the server piece of shiny.
renderChart_pct <- function(expr, env = parent.frame(), quoted = FALSE) {
func <- shiny::exprToFunction(expr, env, quoted)
function() {
rChart_ <- func()
cht_style <- sprintf("<style>.rChart {width: %s; height: %s} </style>",
#### change these here to desired %
"100%", "100%")
cht <- paste(capture.output(rChart_$print()), collapse = '\n')
HTML(paste(c(cht_style, cht), collapse = '\n'))
}
}
Then use renderChart_pct instead of renderChart2. You'll probably notice that the result will not be truly responsive. Altogether, here would be an example with dPlot.
library(shiny)
library(rCharts)
renderChart_pct <- function(expr, env = parent.frame(), quoted = FALSE) {
func <- shiny::exprToFunction(expr, env, quoted)
function() {
rChart_ <- func()
cht_style <- sprintf("<style>.rChart {width: %s; height: %s} </style>",
"100%", "100%")
cht <- paste(capture.output(rChart_$print()), collapse = '\n')
HTML(paste(c(cht_style, cht), collapse = '\n'))
}
}
ui = shinyUI(fluidPage(
fluidRow(
column(4,
h1("Filler Header")
)
,column(8,
div (
showOutput("myChart", "dimple")
)
)
)
))
server = function(input,output){
output$myChart <- renderChart_pct({
d = dPlot(
x~x
, data = data.frame(x=1:10)
, type = "line"
, height = 500
, width = NULL
)
})
}
runApp( list(ui=ui,server=server))
Related
I have a highcharts sankey diagram with two sides:
There are situations where some of my nodes have empty links (=with 0 weight). I would like the node to being displayed despite having no link from or to it.
Any chance I can achieve this?
I read on this thread that I have to fake it with weight=1 connexions, I could make the link transparent, and twitch the tooltip to hide those, but that's very painful for something that feels pretty basic.
Maybe a custom call of the generateNode call or something?
Thanks for the help
You can use the following wrap to show a node when the weight is 0.
const isObject = Highcharts.isObject,
merge = Highcharts.merge
function getDLOptions(
params
) {
const optionsPoint = (
isObject(params.optionsPoint) ?
params.optionsPoint.dataLabels : {}
),
optionsLevel = (
isObject(params.level) ?
params.level.dataLabels : {}
),
options = merge({
style: {}
}, optionsLevel, optionsPoint);
return options;
}
Highcharts.wrap(
Highcharts.seriesTypes.sankey.prototype,
'translateNode',
function(proceed, node, column) {
var translationFactor = this.translationFactor,
series = this,
chart = this.chart,
options = this.options,
sum = node.getSum(),
nodeHeight = Math.max(Math.round(sum * translationFactor),
this.options.minLinkWidth),
nodeWidth = Math.round(this.nodeWidth),
crisp = Math.round(options.borderWidth) % 2 / 2,
nodeOffset = column.sankeyColumn.offset(node,
translationFactor),
fromNodeTop = Math.floor(Highcharts.pick(nodeOffset.absoluteTop, (column.sankeyColumn.top(translationFactor) +
nodeOffset.relativeTop))) + crisp,
left = Math.floor(this.colDistance * node.column +
options.borderWidth / 2) + Highcharts.relativeLength(node.options.offsetHorizontal || 0,
nodeWidth) +
crisp,
nodeLeft = chart.inverted ?
chart.plotSizeX - left :
left;
node.sum = sum;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (1) {
// Draw the node
node.shapeType = 'rect';
node.nodeX = nodeLeft;
node.nodeY = fromNodeTop;
let x = nodeLeft,
y = fromNodeTop,
width = node.options.width || options.width || nodeWidth,
height = node.options.height || options.height || nodeHeight;
if (chart.inverted) {
x = nodeLeft - nodeWidth;
y = chart.plotSizeY - fromNodeTop - nodeHeight;
width = node.options.height || options.height || nodeWidth;
height = node.options.width || options.width || nodeHeight;
}
// Calculate data label options for the point
node.dlOptions = getDLOptions({
level: (this.mapOptionsToLevel)[node.level],
optionsPoint: node.options
});
// Pass test in drawPoints
node.plotX = 1;
node.plotY = 1;
// Set the anchor position for tooltips
node.tooltipPos = chart.inverted ? [
(chart.plotSizeY) - y - height / 2,
(chart.plotSizeX) - x - width / 2
] : [
x + width / 2,
y + height / 2
];
node.shapeArgs = {
x,
y,
width,
height,
display: node.hasShape() ? '' : 'none'
};
} else {
node.dlOptions = {
enabled: false
};
}
}
);
Demo:
http://jsfiddle.net/BlackLabel/uh6fp89j/
In the above solution, another node arrangement would be difficult to achieve and may require a lot of modifications beyond our scope of support.
You can consider using mentioned "tricky solution", since might return a better positioning result. This solution is based on changing 0 weight nodes on the chart.load() event and converting the tooltip as well, so it may require adjustment to your project.
chart: {
events: {
load() {
this.series[0].points.forEach(point => {
if (point.weight === 0) {
point.update({
weight: 0.1,
color: 'transparent'
})
}
})
}
}
},
tooltip: {
nodeFormatter: function() {
return `${this.name}: <b>${Math.floor(this.sum)}</b><br/>`
},
pointFormatter: function() {
return `${this.fromNode.name} → ${this.toNode.name}: <b>${Math.floor(this.weight)}</b><br/>`
}
},
Demo:
http://jsfiddle.net/BlackLabel/0dqpabku/
I am using highcharter library. I want to split (or filter) other charts and tables based on selection in particular category of pie (doughnut) chart. My below code is working fine.
Desired result - When user clicks on pie again after selection, it should remove filtering. Below code captures last clicked category and it matches with the current selection using curre() and lstre() reactive values.
Issue When user clicks on pie more than twice, last clicked category matches with the current selected category so it does not perform any filtering.
I tried hc_add_event_point(event = "unselect"), it does not let user select particular category of pie more than twice.
library("shiny")
library("highcharter")
library(dplyr)
ui <- shinyUI(
fluidPage(
column(width = 8, highchartOutput("hcontainer", height = "500px")),
column(width = 4, textOutput("text")),
column(width = 4, dataTableOutput('temptable')))
)
server <- function(input, output) {
a <- data.frame(b = LETTERS[1:5], c = 11:15)
aa <- data.frame(b = LETTERS[1:5])
output$hcontainer <- renderHighchart({
canvasClickFunction <- JS("function(event) {Shiny.setInputValue('canvasClicked', [this.name, event.point.name, Math.random()]);}")
legendClickFunction <- JS("function(event) {Shiny.setInputValue('legendClicked', this.name);}")
highchart() %>%
hc_chart(type="pie") %>%
hc_add_series_labels_values(labels = a$b, values = a$c,
innerSize = '60%',
allowPointSelect= TRUE,
slicedOffset = 20,
states = list(
select = list(
color= NULL,
borderWidth = 5,
borderColor = '#ccc'
))) %>%
hc_plotOptions(series = list(
events = list(click = canvasClickFunction,
legendItemClick = legendClickFunction))) %>%
hc_add_event_point(event = "unselect")
})
makeReactiveBinding("outputText")
rv <- reactiveValues(lstval=0,curval=0)
observeEvent(input$canvasClicked[2], {
rv$lstval <- rv$curval;
rv$curval <- input$canvasClicked[2]}
)
curre <- reactive({req(input$canvasClicked[2]); input$canvasClicked[2]; rv$curval})
lstre <- reactive({req(input$canvasClicked[2]); input$canvasClicked[2]; rv$lstval})
observeEvent(input$canvasClicked, {
outputText <<- paste0("You clicked on series ", input$canvasClicked[1], " and the bar you clicked was from category ", input$canvasClicked[2],
input$plot_hc_unselect, ".")
})
observeEvent(input$legendClicked, {
outputText <<- paste0("You clicked into the legend and selected series ", input$legendClicked, ".")
})
output$text <- renderText({
outputText
})
output$temptable <- renderDataTable(
if (length(input$canvasClicked[2])>0) {
if (curre()!=lstre())
aa %>% filter(b==input$canvasClicked[2])
else {
aa
}
}
else {aa}
)
}
shinyApp(ui, server)
I want to import data of a similar category from multiple source files.
Every source has a short label.
How can I incorporate this into drake, without writing out every file as its own target?
I thought the following would work, but it does not. Ideally, I would like to have the targets raw_a and raw_b.
input_files <- list(
'a' = 'file_1.csv',
'b' = 'file_2.csv'
)
plan <-
drake::drake_plan(
raw = drake::target(
import_file(file),
transform = map(
file = file_in(!! input_files)
)
)
)
with
import_file <- function(file) {
readr::read_csv(file, skip = 2)
}
You are so close. file_in() needs to go literally in the command, not the transformation.
library(drake)
input_files <- c("file_1.csv", "file_2.csv")
plan <- drake_plan(
raw = target(
import_file(file_in(file)),
transform = map(file = !!input_files)
)
)
config <- drake_config(plan)
vis_drake_graph(config)
Created on 2019-10-19 by the reprex package (v0.3.0)
This is probably the idiomatic solution.
plan <-
drake::drake_plan(
raw = drake::target(
import_file(file),
transform = map(
file = file_in('file_1.csv', 'file_2.csv'),
label = c('a', 'b'),
.id = label
)
)
)
file_in needs to around the whole string
plan <-
drake::drake_plan(
raw = drake::target(
import_file(file),
transform = map(
file = list(
file_in('file_1.csv'),
file_in('file_2.csv')
)
)
)
)
I implemented a <textarea> in Elm such that tabs indent and unindent instead of change focus to another HTML element. Works great except that unindenting sometimes causes the selection to disappear! If I'm selecting the 5th character to the 12th character, I press shift-tab, then it removes 2 tab characters, but it also makes the selection change to a cursor at position 10. The selection range should remain the same..
I have an SSCCE at Ellie: https://ellie-app.com/3x2qQdLqpHga1/2
Here are some screenshots to illustrate the problem. Pressing Setup shows this:
Then pressing Unindent should show the following (with the selection of "def\ng" still intact):
Unfortunately, pressing Unindent actually shows the following. The text is unindented fine, but the selection range goes away and there's just a cursor between the g and the h:
Interesting issue and excellent problem illustration!
The problem is that for some reason re-rendering doesn't occur when one of the selectionStart/selectionEnd properties remains the same. Try changing 5 to 6 on line #42.
It works when you introduce a forced reflow in the element structure. See here: https://ellie-app.com/6Q7h7Lm9XRya1 (I updated it to 0.19 to see if that would solve the problem, but it didn't).
Note that this probably re-renders the whole textarea anew so it might cause problems if the textarea is a huge piece of code. You could solve that by alternating between two identical textareas where you toggle their visibility every render.
module Main exposing (Model, Msg(..), main, update, view)
-- Note: this is Elm 0.19
import Browser
import Browser.Dom exposing (focus)
import Html exposing (Html, button, div, text, textarea)
import Html.Attributes exposing (attribute, class, cols, id, property, rows, style, value)
import Html.Events exposing (onClick)
import Html.Lazy exposing (lazy2)
import Json.Encode as Encode
import Task exposing (attempt)
type alias Model =
{ content : String
, selectionStart : Int
, selectionEnd : Int
-- keep counter of renderings for purposes of randomness in rendering loop
, renderCounter : Int
}
main =
Browser.element
{ init = initModel
, view = view
, update = update
, subscriptions = \s -> Sub.none
}
initModel : () -> ( Model, Cmd Msg )
initModel flags =
( Model "" 0 0 0, Cmd.batch [] )
type Msg
= Setup
| Unindent
| NoOp (Result Browser.Dom.Error ())
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
let
newRenderCounter =
model.renderCounter + 1
in
case msg of
Setup ->
( { model
| content = "\tabc\n\tdef\n\tghi"
, selectionStart = 5
, selectionEnd = 12
, renderCounter = newRenderCounter
}
, attempt NoOp <| focus "ta"
)
Unindent ->
( { model
| content = "\tabc\ndef\nghi"
, selectionStart = 5
, selectionEnd = 10
, renderCounter = newRenderCounter
}
, attempt NoOp <| focus "ta"
)
NoOp _ ->
( model, Cmd.batch [] )
view : Model -> Html Msg
view model =
div []
(viewTextarea model model.renderCounter
++ [ button [ onClick Setup ] [ text "Setup" ]
, button [ onClick Unindent ] [ text "Unindent" ]
]
)
viewTextarea : Model -> Int -> List (Html msg)
viewTextarea model counter =
let
rerenderForcer =
div [attribute "style" "display: none;"] []
ta =
textarea
[ id "ta"
, cols 40
, rows 20
, value model.content
, property "selectionStart" <| Encode.int model.selectionStart
, property "selectionEnd" <| Encode.int model.selectionEnd
]
[]
in
-- this is the clue. by alternating this every render, it seems to force Elm to render the textarea anew, fixing the issue. Probably not very performant though. For a performant version, use an identical textarea instead of the div and make sure the two selectionStart/end properties both differ from the previous iteration. Then alternate visibility between the two every iteration.
if isEven counter then
[ ta, rerenderForcer ]
else
[ rerenderForcer, ta ]
isEven : Int -> Bool
isEven i =
modBy 2 i == 0
How would you write a Parsing Expression Grammar in any of the following Parser Generators (PEG.js, Citrus, Treetop) which can handle Python/Haskell/CoffeScript style indentation:
Examples of a not-yet-existing programming language:
square x =
x * x
cube x =
x * square x
fib n =
if n <= 1
0
else
fib(n - 2) + fib(n - 1) # some cheating allowed here with brackets
Update:
Don't try to write an interpreter for the examples above. I'm only interested in the indentation problem. Another example might be parsing the following:
foo
bar = 1
baz = 2
tap
zap = 3
# should yield (ruby style hashmap):
# {:foo => { :bar => 1, :baz => 2}, :tap => { :zap => 3 } }
Pure PEG cannot parse indentation.
But peg.js can.
I did a quick-and-dirty experiment (being inspired by Ira Baxter's comment about cheating) and wrote a simple tokenizer.
For a more complete solution (a complete parser) please see this question: Parse indentation level with PEG.js
/* Initializations */
{
function start(first, tail) {
var done = [first[1]];
for (var i = 0; i < tail.length; i++) {
done = done.concat(tail[i][1][0])
done.push(tail[i][1][1]);
}
return done;
}
var depths = [0];
function indent(s) {
var depth = s.length;
if (depth == depths[0]) return [];
if (depth > depths[0]) {
depths.unshift(depth);
return ["INDENT"];
}
var dents = [];
while (depth < depths[0]) {
depths.shift();
dents.push("DEDENT");
}
if (depth != depths[0]) dents.push("BADDENT");
return dents;
}
}
/* The real grammar */
start = first:line tail:(newline line)* newline? { return start(first, tail) }
line = depth:indent s:text { return [depth, s] }
indent = s:" "* { return indent(s) }
text = c:[^\n]* { return c.join("") }
newline = "\n" {}
depths is a stack of indentations. indent() gives back an array of indentation tokens and start() unwraps the array to make the parser behave somewhat like a stream.
peg.js produces for the text:
alpha
beta
gamma
delta
epsilon
zeta
eta
theta
iota
these results:
[
"alpha",
"INDENT",
"beta",
"gamma",
"INDENT",
"delta",
"DEDENT",
"DEDENT",
"epsilon",
"INDENT",
"zeta",
"DEDENT",
"BADDENT",
"eta",
"theta",
"INDENT",
"iota",
"DEDENT",
"",
""
]
This tokenizer even catches bad indents.
I think an indentation-sensitive language like that is context-sensitive. I believe PEG can only do context-free langauges.
Note that, while nalply's answer is certainly correct that PEG.js can do it via external state (ie the dreaded global variables), it can be a dangerous path to walk down (worse than the usual problems with global variables). Some rules can initially match (and then run their actions) but parent rules can fail thus causing the action run to be invalid. If external state is changed in such an action, you can end up with invalid state. This is super awful, and could lead to tremors, vomiting, and death. Some issues and solutions to this are in the comments here: https://github.com/dmajda/pegjs/issues/45
So what we are really doing here with indentation is creating something like a C-style blocks which often have their own lexical scope. If I were writing a compiler for a language like that I think I would try and have the lexer keep track of the indentation. Every time the indentation increases it could insert a '{' token. Likewise every time it decreases it could inset an '}' token. Then writing an expression grammar with explicit curly braces to represent lexical scope becomes more straight forward.
You can do this in Treetop by using semantic predicates. In this case you need a semantic predicate that detects closing a white-space indented block due to the occurrence of another line that has the same or lesser indentation. The predicate must count the indentation from the opening line, and return true (block closed) if the current line's indentation has finished at the same or shorter length. Because the closing condition is context-dependent, it must not be memoized.
Here's the example code I'm about to add to Treetop's documentation. Note that I've overridden Treetop's SyntaxNode inspect method to make it easier to visualise the result.
grammar IndentedBlocks
rule top
# Initialise the indent stack with a sentinel:
&{|s| #indents = [-1] }
nested_blocks
{
def inspect
nested_blocks.inspect
end
}
end
rule nested_blocks
(
# Do not try to extract this semantic predicate into a new rule.
# It will be memo-ized incorrectly because #indents.last will change.
!{|s|
# Peek at the following indentation:
save = index; i = _nt_indentation; index = save
# We're closing if the indentation is less or the same as our enclosing block's:
closing = i.text_value.length <= #indents.last
}
block
)*
{
def inspect
elements.map{|e| e.block.inspect}*"\n"
end
}
end
rule block
indented_line # The block's opening line
&{|s| # Push the indent level to the stack
level = s[0].indentation.text_value.length
#indents << level
true
}
nested_blocks # Parse any nested blocks
&{|s| # Pop the indent stack
# Note that under no circumstances should "nested_blocks" fail, or the stack will be mis-aligned
#indents.pop
true
}
{
def inspect
indented_line.inspect +
(nested_blocks.elements.size > 0 ? (
"\n{\n" +
nested_blocks.elements.map { |content|
content.block.inspect+"\n"
}*'' +
"}"
)
: "")
end
}
end
rule indented_line
indentation text:((!"\n" .)*) "\n"
{
def inspect
text.text_value
end
}
end
rule indentation
' '*
end
end
Here's a little test driver program so you can try it easily:
require 'polyglot'
require 'treetop'
require 'indented_blocks'
parser = IndentedBlocksParser.new
input = <<END
def foo
here is some indented text
here it's further indented
and here the same
but here it's further again
and some more like that
before going back to here
down again
back twice
and start from the beginning again
with only a small block this time
END
parse_tree = parser.parse input
p parse_tree
I know this is an old thread, but I just wanted to add some PEGjs code to the answers. This code will parse a piece of text and "nest" it into a sort of "AST-ish" structure. It only goes one deep and it looks ugly, furthermore it does not really use the return values to create the right structure but keeps an in-memory tree of your syntax and it will return that at the end. This might well become unwieldy and cause some performance issues, but at least it does what it's supposed to.
Note: Make sure you have tabs instead of spaces!
{
var indentStack = [],
rootScope = {
value: "PROGRAM",
values: [],
scopes: []
};
function addToRootScope(text) {
// Here we wiggle with the form and append the new
// scope to the rootScope.
if (!text) return;
if (indentStack.length === 0) {
rootScope.scopes.unshift({
text: text,
statements: []
});
}
else {
rootScope.scopes[0].statements.push(text);
}
}
}
/* Add some grammar */
start
= lines: (line EOL+)*
{
return rootScope;
}
line
= line: (samedent t:text { addToRootScope(t); }) &EOL
/ line: (indent t:text { addToRootScope(t); }) &EOL
/ line: (dedent t:text { addToRootScope(t); }) &EOL
/ line: [ \t]* &EOL
/ EOF
samedent
= i:[\t]* &{ return i.length === indentStack.length; }
{
console.log("s:", i.length, " level:", indentStack.length);
}
indent
= i:[\t]+ &{ return i.length > indentStack.length; }
{
indentStack.push("");
console.log("i:", i.length, " level:", indentStack.length);
}
dedent
= i:[\t]* &{ return i.length < indentStack.length; }
{
for (var j = 0; j < i.length + 1; j++) {
indentStack.pop();
}
console.log("d:", i.length + 1, " level:", indentStack.length);
}
text
= numbers: number+ { return numbers.join(""); }
/ txt: character+ { return txt.join(""); }
number
= $[0-9]
character
= $[ a-zA-Z->+]
__
= [ ]+
_
= [ ]*
EOF
= !.
EOL
= "\r\n"
/ "\n"
/ "\r"