Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Package: LDAvis
Title: Interactive Visualization of Topic Models
Version: 0.3.4
Version: 0.3.5
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"), email =
"cpsievert1@gmail.com", comment = c(ORCID = "0000-0002-4958-2844")), person("Kenny", "Shirley", role = "aut",
email = "kshirley@research.att.com"))
email = "kshirley@research.att.com"), person("Christopher", "Baker", role = "ctb", email = "chriscrewbaker@gmail.com"))
Description: Tools to create an interactive web-based visualization of a
topic model that has been fit to a corpus of text data using
Latent Dirichlet Allocation (LDA). Given the estimated parameters of
Expand Down
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
CHANGES IN LDAvis VERSION 0.3.5

Click events (on topics and terms) are now made available to the Shiny input object (closes [[#45]](https://github.com/cpsievert/LDAvis/issues/45)). When a topic is clicked on, the topic number is made available to Shiny server at input$OUTPUTID_topic_click, where OUTPUTID is the outputId of the LDAvis plot. When a term is clicked on, the term is available at input$OUTPUTID_term_click. See https://github.com/cpsievert/LDAvis/tree/master/inst/examples/shiny for an example Shiny app.

CHANGES IN LDAvis VERSION 0.3.4

A new argument, encoding, was added to serVis(). This sets the encoding which will be used to write the JSON file. This may be set to `UTF-8` to fix an issue where, when running on windows, the JSON output would be encoded in ANSI despite the R data beeing encoded in UTF-8. This should also fix #50.
Expand Down
12 changes: 11 additions & 1 deletion inst/examples/shiny/server.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@ shinyServer(function(input, output, session) {
with(TwentyNewsgroups,
createJSON(phi, theta, doc.length, vocab, term.frequency,
R = input$nTerms))})
})

output$termClicked <- renderPrint({
if (is.null(input$myChart_term_click)) return()
paste("You clicked on term:", input$myChart_term_click)
})
output$topicClicked <- renderPrint({
if (is.null(input$myChart_topic_click)) return()
paste("You clicked on topic:", input$myChart_topic_click)
})

})
2 changes: 2 additions & 0 deletions inst/examples/shiny/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ library(LDAvis)
shinyUI(
fluidPage(
sliderInput("nTerms", "Number of terms to display", min = 20, max = 40, value = 30),
textOutput("termClicked"),
textOutput("topicClicked"),
visOutput('myChart')
)
)
124 changes: 119 additions & 5 deletions inst/htmljs/ldavis.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ LDAvis = function(to_select, json_file) {
vis_state = {
lambda: 1,
topic: 0,
term: ""
term: "",
topic_clicked: 0,
term_clicked: ""
};

// Set up a few 'global' variables to hold the data:
Expand Down Expand Up @@ -89,7 +91,16 @@ LDAvis = function(to_select, json_file) {
return 0;
};
}


// see if vis is being run as part of a shiny app
// (code adapted from https://github.com/ramnathv/htmlwidgets/blob/master/inst/www/htmlwidgets.js#L15)
var inShinyMode = typeof(window.Shiny) !== "undefined" && !!window.Shiny.outputBindings;

if (inShinyMode) {
var outputId = to_select.substring(1, to_select.length);
var shinyClickedTopic = outputId + "_topic_click";
var shinyClickedTerm = outputId + "_term_click";
}

// The actual read-in of the data and main code:
d3.json(json_file, function(error, data) {
Expand Down Expand Up @@ -167,9 +178,11 @@ LDAvis = function(to_select, json_file) {
// increment the value in the input box
document.getElementById(topicID).value = value_new;
topic_off(document.getElementById(topicID + value_old));
topic_on(document.getElementById(topicID + value_new));
var oldtopic = document.getElementById(topicID + value_new);
topic_on(oldtopic);
vis_state.topic = value_new;
state_save(true);
topic_click(oldtopic, value_new);
})

d3.select("#" + topicDown)
Expand All @@ -183,9 +196,11 @@ LDAvis = function(to_select, json_file) {
// increment the value in the input box
document.getElementById(topicID).value = value_new;
topic_off(document.getElementById(topicID + value_old));
topic_on(document.getElementById(topicID + value_new));
var oldtopic = document.getElementById(topicID + value_new);
topic_on(oldtopic);
vis_state.topic = value_new;
state_save(true);
topic_click(oldtopic, value_new);
})

d3.select("#" + topicID)
Expand All @@ -198,10 +213,12 @@ LDAvis = function(to_select, json_file) {
var value_new = document.getElementById(topicID).value;
if (!isNaN(value_new) && value_new > 0) {
value_new = Math.min(K, Math.max(1, value_new))
topic_on(document.getElementById(topicID + value_new));
var oldtopic = document.getElementById(topicID + value_new);
topic_on(oldtopic);
vis_state.topic = value_new;
state_save(true);
document.getElementById(topicID).value = vis_state.topic;
topic_click(oldtopic, value_new);
}
})

Expand Down Expand Up @@ -415,6 +432,7 @@ LDAvis = function(to_select, json_file) {
document.getElementById(topicID).value = vis_state.topic = d.topics;
state_save(true);
topic_on(this);
topic_click(this, d.topics);
})
.on("mouseout", function(d) {
if (vis_state.topic != d.topics) topic_off(this);
Expand Down Expand Up @@ -549,6 +567,9 @@ LDAvis = function(to_select, json_file) {
// term_on(this);
// debugger;
// })
.on("click", function(d) {
term_click(this, d.Term)
})
.on("mouseout", function() {
vis_state.term = "";
term_off(this);
Expand Down Expand Up @@ -992,6 +1013,17 @@ LDAvis = function(to_select, json_file) {
function topic_on(circle) {
if (circle == null) return null;

// whenever the topic changes we have to remove the underline style
// from any clicked term
var old_term_clicked_id = termID + vis_state.term_clicked;
var topic_clicked_id = topicID + vis_state.topic_clicked;
if (vis_state.term_clicked != "" && circle.id != topic_clicked_id) {
var oldterm = document.getElementById(old_term_clicked_id);
if (oldterm != null) {
oldterm.style.textDecoration = null;
}
}

// grab data bound to this element
var d = circle.__data__
var Freq = Math.round(d.Freq * 10) / 10,
Expand Down Expand Up @@ -1343,15 +1375,97 @@ LDAvis = function(to_select, json_file) {
}

function state_reset() {

// set the style of any clicked term back to be non-underline
var old_term_clicked_id = termID + vis_state.term_clicked;
if (vis_state.term_clicked != "") {
var oldterm = document.getElementById(old_term_clicked_id);
if (oldterm != null) {
oldterm.style.textDecoration = null;
}
}

if (vis_state.topic > 0) {
topic_off(document.getElementById(topicID + vis_state.topic));
// set the style of any topic clicked to be back to regular style
// (no thick border around topic circle)
var old_topic_clicked_id = topicID + vis_state.topic_clicked;
if (vis_state.topic_clicked > 0) {
document.getElementById(old_topic_clicked_id).style.strokeWidth = null;
}
}
if (vis_state.term != "") {
term_off(document.getElementById(termID + vis_state.term));
}
vis_state.term = "";
document.getElementById(topicID).value = vis_state.topic = 0;
state_save(true);

// make sure term ids are all correct
d3.selectAll(".terms").attr("id", function(d) {
return (termID + d.Term)
});

// update shiny inputs to be null
if (inShinyMode) {
Shiny.onInputChange(shinyClickedTopic, null);
Shiny.onInputChange(shinyClickedTerm, null);
}

// set state of topic_clicked to 0, so we can click on topic x, reset
// vis, then click on topic x again without any problems
vis_state.topic_clicked = 0;
}

function topic_click(newtopic, newtopic_num) {
if (!inShinyMode) {
return null;
}
// set style of clicked topic to have thicker border
newtopic.style.strokeWidth = 2;

// set style of old selected topic back to regular border
var old_topic_clicked_id = topicID + vis_state.topic_clicked;
if (vis_state.topic_clicked > 0 && old_topic_clicked_id != this.id) {
document.getElementById(old_topic_clicked_id).style.strokeWidth = null;
}

// save state of topic clicked
vis_state.topic_clicked = newtopic_num;

// update shiny topic input object to be new topic clicked
Shiny.onInputChange(shinyClickedTopic, newtopic_num);

// since topic changed, we want to reset the input term object back to null
Shiny.onInputChange(shinyClickedTerm, null);
}

function term_click(newterm, newterm_term) {
if (!inShinyMode) {
return null;
}
// make sure term ids are up to date
d3.selectAll(".terms").attr("id", function(d) {
return (termID + d.Term)
});

// underline clicked term
newterm.style.textDecoration = "underline";

// set style of old clicked term back to non-underline
var old_term_clicked_id = termID + vis_state.term_clicked;
if (old_term_clicked_id != newterm.id) {
var oldterm = document.getElementById(old_term_clicked_id);
if (oldterm != null) {
oldterm.style.textDecoration = null;
}
}

// save state of term clicked
vis_state.term_clicked = newterm_term;

// update shiny term input object to know about new term clicked
Shiny.onInputChange(shinyClickedTerm, newterm_term);
}

});
Expand Down