Application brillante qui permet aux utilisateurs de modifier les données
ce n'est pas encore une question pratique mais plutôt théorique. Je pensais utiliser brillant pour afficher des données brutes d'une manière interactive. C'est très bien.
Cependant - est-il possible d'avoir les utilisateurs à modifier les données affichées?
dire, si j'ai un tas de curseurs pour les utilisateurs de restreindre les données sous-jacentes pour satisfaire certaines conditions et avoir ces observations affichées - est-il possible de permettre aux utilisateurs de faire les modifications de données et ces modifications envoyées au serveur, qui enregistre ces modifications?
je pense à des scénarios où les utilisateurs peuvent utiliser une Application brillante pour parcourir les données et détecter des valeurs aberrantes potentielles dans les données -- l'utilisateur peut alors les signaler comme étant des valeurs aberrantes. Toutefois, cette information doit être transmise au serveur.
une telle application est-elle possible? Existe-il des exemples existants?
2 réponses
vous pouvez essentiellement faire presque tout en brillant puisque vous pouvez créer votre propre input et output fixations – donc la réponse à votre question est oui, ce que vous demandez est possible. Disons que vous avez une trame de données que vous envoyez à une page web pour être visible par l'utilisateur. Comme un exemple, vous voulez permettre aux utilisateurs de simplement cliquer sur une cellule si c'est une exception qui doit être supprimée (remplacée par NA
).
disons la base de données ressemble à ceci:
x <- data.frame(Age = c(10, 20, 1000), Weight = c(120, 131, 111))
x
# Age Weight
# 10 120
# 20 131
# 1000 111
de shiny vous construiriez une table HTML normale qui pourrait ressembler à quelque chose comme ceci quand affiché sur la page Web:
<table class="outlier-finder" id="outliers">
<tr>
<td>Age</td>
<td>Weight</td>
</tr>
<tr>
<td>10</td>
<td>120</td>
</tr>
<tr>
<td>20</td>
<td>131</td>
</tr>
<tr>
<td>1000</td>
<td>111</td>
</tr>
</table>
maintenant, sortez le jQuery et liez un événement de clic de sorte que lorsqu'une cellule est cliquée, vous pouvez enregistrer le numéro de la ligne et de la colonne ( voir ici ), puis remplacer cette cellule par NA
en brillant. Votre entrée contraignant pourrait ressembler à quelque chose comme ( voir ici pour plus de détails sur ce qui se passe ici ):
$(document).on("click", ".outlier-finder td", function(evt) {
// Identify the clicked cell.
var el = $(evt.target);
// Raise an event to signal that the something has been selected.
el.trigger("change");
});
var cell_binding = new Shiny.InputBinding();
$.extend(cell_binding, {
find: function(scope) {
return $(scope).find(".outlier-finder td");
},
getValue: function(el) {
// Get the row and cell number of the selected td.
var col = el.parent().children().index(el);
var row = el.parent().parent().children().index(el.parent());
var result = [row, col];
return result;
},
setValue: function(el, value) {
$(el).text(value);
},
subscribe: function(el, callback) {
$(el).on("change.cell_binding", function(e) {
callback();
});
},
unsubscribe: function(el) {
$(el).off(".cell_binding");
}
});
Shiny.inputBindings.register(cell_binding);
il y a beaucoup de choses qui se passent ici, mais en général ces reliures d'entrée sont assez semblables les unes aux autres. La chose la plus importante est la fonction setValue()
. Ce qui devrait se passer là-bas (ceci n'a pas été testé) est la ligne et le numéro de colonne de la cellule cliquée est enregistré et envoyé au serveur.
alors de Shiny vous feriez simplement quelque chose comme:
updateData <- reactive({
# Get selection
remove_outlier <- as.integer(RJSONIO::fromJSON(input$outliers))
if (!is.null(remove_outlier)) {
# Remove that outlier.
x[remove_outlier[1], remove_outlier[2]] <- NA
}
return(x)
})
output$outliers <- renderText({
# Update x.
current_x <- updateData()
# Write code to output current_x to page.
# ...
# ...
})
vous aurez probablement besoin de faire une liaison de sortie pour la sortie$outliers aussi bien. C'est le code simplifié ici évidemment, vous devriez appliquer la vérification d'erreur etc.
ce n'est qu'un exemple. En réalité, vous n'auriez probablement pas mis à jour brillamment votre base de données chaque fois qu'un utilisateur fait un changement. Vous voulez avoir une sorte de bouton d'envoi, de sorte qu'une fois que l'utilisateur a effectué tous ses changements, ils peuvent être appliqués.
Je n'ai même pas testé à distance tout ce donc il ya presque certainement quelques erreurs. Mais puisque tu posais une question théorique, je n'ai pas trop vérifié. La tactique générale devrait marcher de toute façon. Avec les liaisons d'entrée, vous pouvez obtenir n'importe quoi d'une page web vers le serveur et vice versa avec les liaisons de sortie. Peut-être en disant "rien" est un étirement, mais vous pouvez faire beaucoup.
j'ai travaillé sur un paquet qui utilise ce workflow:
- l'utilisateur charge les données dans la session R et effectue une sélection initiale à partir de la ligne de commande
- les données sont passées à une application brillante qui permet à l'utilisateur de sélectionner et de modifier les données de façon interactive."
- l'utilisateur clique sur un bouton pour terminer la session brillante, et les données modifiées sont retournées à la session R, avec tous les changements effectués par le l'utilisateur intacte.
ce n'est pas la façon habituelle Shiny est utilisé - l'application n'est pas déployée à distance, mais plutôt est utilisé localement pour servir d'interface de pointage interactif pour un seul utilisateur. J'ai fait des choses similaires avec les graphismes de base et la fonction locator()
, qui est fastidieuse. Il peut être plus facile à utiliser tcl/tk, mais j'étais curieux de voir comment cela pourrait fonctionner avec Brillant.
voici un exemple de jouet:
myShiny <- function(mydata){
ui <- fluidPage(
actionButton("exit", label = "Return to R"),
plotOutput("dataPlot", click = "pointPicker")
)
server <- function(input, output){
output$dataPlot <- renderPlot({
plot(x = myData()[, 1], y = myData()[,2], cex = myData()[,3])
})
myData <- reactive({
selPts <- nearPoints(mydata,
input$pointPicker, "x", "y",
threshold = 25, maxpoints = 1, allRows = TRUE)
if(sum(selPts[,"selected_"]) > 0){
## use '<<-' to modify mydata in the parent environment, not the
## local copy
mydata[which(selPts[, "selected_", ]), "size"] <<-
mydata[which(selPts[, "selected_", ]), "size"] + 1
}
mydata
})
observe({
if(input$exit > 0)
stopApp()
})
}
runApp(shinyApp(ui = ui, server = server))
return(mydata)
}
testDF <- data.frame(x = seq(0, 2 * pi, length = 13),
y = sin(seq(0, 2 * pi, length = 13)),
size = rep(1, 13))
modDF <- myShiny(testDF)
dans ce cas, cliquer sur un point augmente la valeur d'une des colonnes ("size") dans la rangée correspondante (qui est visualisée en utilisant l'argument cex
quand tracée). Les valeurs sont retournées à l'utilisateur et, dans ce cas, stockées dans la variable modDF
:
> modDF
x y size
1 0.0000000 0.000000e+00 1
2 0.5235988 5.000000e-01 5
3 1.0471976 8.660254e-01 1
4 1.5707963 1.000000e+00 1
5 2.0943951 8.660254e-01 2
6 2.6179939 5.000000e-01 1
7 3.1415927 1.224647e-16 1
8 3.6651914 -5.000000e-01 7
9 4.1887902 -8.660254e-01 1
10 4.7123890 -1.000000e+00 1
11 5.2359878 -8.660254e-01 3
12 5.7595865 -5.000000e-01 1
13 6.2831853 -2.449294e-16 1
il serait facile de modifier ceci pour basculer la valeur dans une colonne "valeur aberrante" (de sorte que vous vous pouvez revenir sur votre décision) ou apporter directement des changements permanents à la base de données.
dans mon paquet actuel, j'utilise cette approche pour permettre à l'utilisateur de sélectionner visuellement les paramètres initiaux pour une régression non linéaire, voir immédiatement l'ajustement du modèle résultant tracé dans le navigateur, répéter jusqu'à ce qu'ils obtiennent un modèle ajusté qui semble raisonnable, et enfin enregistrer les résultats et de revenir à leur session R.