2013-06-23 12 views
21

To nie jest jeszcze pytanie praktyczne, a raczej teoretyczne. Myślałem o użyciu Shiny do wyświetlania surowych danych w sposób interaktywny. Jest okej.Aplikacja Shiny R, która pozwala użytkownikom modyfikować dane.

Jednak czy użytkownik może zmienić wyświetlane dane?

Powiedz, jeśli mam kilka suwaków, aby użytkownicy mogli ograniczyć dane bazowe, aby spełnić określone warunki i mieć wyświetlone te obserwacje - czy możliwe jest zezwolenie użytkownikom na modyfikowanie tych danych i czy te modyfikacje są wysyłane z powrotem do serwer, który z kolei zapisuje te zmiany?

Mam na myśli scenariusze, w których użytkownicy mogą używać Błyszczącej aplikacji do przeglądania danych i wykrywania potencjalnych wartości odstających w danych - użytkownik może następnie oznaczyć je jako odstające. Jednak te informacje muszą zostać przekazane z powrotem do serwera.

Czy taka aplikacja jest możliwa? Czy są jakieś istniejące przykłady?

+1

Domyślam się, że trudnością jest posiadanie edytowalnej tabeli danych, nie znam istniejącej. Informacje mogą wrócić do serwera, gdy mamy taką wyświetlaną i edytowalną tabelę danych. – Zhenglei

Odpowiedz

18

Zasadniczo możesz zrobić prawie wszystko w Shiny, ponieważ możesz tworzyć własne wiązania i output, więc odpowiedź na twoje pytanie brzmi "tak", to, o co prosisz, jest możliwe. Załóżmy, że masz ramkę danych, którą wysyłasz na stronę internetową, którą chcesz przeglądać. Na przykład, pozwól użytkownikom po prostu kliknąć komórkę, jeśli jest to usunięcie, które powinno zostać usunięte (zastąpione NA).

Powiedzmy ramka danych wygląda następująco:

x <- data.frame(Age = c(10, 20, 1000), Weight = c(120, 131, 111)) 
x 

# Age Weight 
# 10  120 
# 20  131 
# 1000 111 

Od błyszczące byś zbudować normalną tabelę HTML, który może wyglądać mniej więcej tak, gdy wyświetlany na stronie:

<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> 

Teraz przełamania out jQuery i wiązać zdarzenie click tak, że po kliknięciu komórki można nagrać numer wiersza i kolumny (see here), a następnie zastąpić tę komórkę NA w Shiny. Twój wkład wiążący może wyglądać mniej więcej tak (see here for details of what's going on here):

$(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); 

Dużo się tu dzieje, ale generalnie te powiązania wejściowe są dość podobne do siebie. Najważniejszą rzeczą jest funkcja setValue(). Co powinno się tam dziać (to nie jest testowane) to numer wiersza i kolumny klikniętej komórki jest rejestrowany i przesyłany z powrotem do serwera.

Następnie z Shiny byś po prostu zrobić coś takiego:

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. 
    # ... 
    # ... 

}) 

Prawdopodobnie będzie trzeba dokonać moc wiążącą dla wyjściowych $ skrajnych, jak również. To jest uproszczony kod tutaj oczywiście, musisz zastosować sprawdzanie błędów itp.

To tylko przykład. W rzeczywistości, prawdopodobnie nie będziesz miał Błyszczącej aktualizacji ramek danych za każdym razem, gdy użytkownik wprowadzi zmianę. Będziesz chciał mieć jakiś przycisk przesyłania, aby po wprowadzeniu przez użytkownika wszystkich zmian można było zastosować.


nie mam nawet zdalnie badane któregokolwiek z tym więc są prawie na pewno jakieś błędy. Ale skoro tylko zadałeś teoretyczne pytanie, nie sprawdziłem tego zbyt wiele.Ogólna taktyka powinna i tak zadziałać. Dzięki powiązaniom wejściowym można uzyskać wszystko, od strony WWW z powrotem do serwera i odwrotnie, z powiązaniami wyjściowymi. Może powiedzenie "coś" jest rozciągliwe —, ale możesz zrobić dużo.

0

pracuję nad pakietem, który wykorzystuje ten obieg:

  1. Dane ładunki użytkownika do sesji R i uzupełnia pewne wstępne badania przesiewowe z wiersza poleceń
  2. Dane są przekazywane do Błyszczące aplikacji umożliwia to użytkownikowi interaktywne wybieranie i modyfikowanie danych, użytkownik klika przycisk, aby zakończyć sesję błyszczący, a zmodyfikowane dane są zwracane do sesji R, z wszystkimi zmianami wprowadzonymi przez użytkownika w stanie nienaruszonym.

To nie jest zwykły sposób, w jaki używany jest Shiny - aplikacja nie jest wdrażana zdalnie, ale zamiast tego jest używana lokalnie jako interaktywny interfejs kreślarski dla pojedynczego użytkownika. Zrobiłem podobne rzeczy z podstawową grafiką i funkcją locator(), co jest uciążliwe. Może być łatwiej używać tcl/tk, ale byłem ciekawy, jak to może działać z Shiny.

Oto przykład zabawki:

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) 

shiny-app

W tym przypadku, kliknięcie punktu zwiększenie wartości jednego z tych kolumn („rozmiar”) w danym rzędzie (co uwidacznia za pomocą argumentu cex podczas drukowania). Wartości są zwracane do użytkownika i, w tym przypadku, przechowywane w modDF zmiennej:

> 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 

byłoby łatwo zmodyfikować to, aby przełączać wartość w „odstających” kolumny (tak, że można odwrócić decyzja) lub bezpośrednio wprowadzić stałe zmiany w ramce danych.

W moim rzeczywistym pakiecie używam tego podejścia, aby umożliwić użytkownikowi wizualne wybranie początkowych parametrów dla regresji nieliniowej, natychmiast zobaczyć wynik dopasowania modelu wykreślonego w przeglądarce, powtórzyć, aż otrzymają dopasowany model, który wygląda rozsądnie , a na koniec zapisz wyniki i wróć do ich sesji R.