entfernen von rowID

Wie rufe ich R-Funktionen auf, wie selektiere ich Daten, ich weiß nicht genau ....

Moderatoren: EDi, jogo

retep
Beiträge: 99
Registriert: Do Sep 06, 2018 7:50 pm

Re: entfernen von rowID

Beitrag von retep »

Hallo,

ich hatte über der Woche nicht ganz soviel Zeit mich weiter mit dem Problem zu beschäftigen. Aber gestern Abend bin ich ein kleines Stückchen weiter gekommen. Hier der der neue Code im Anhang das kleine Date frame.

Code: Alles auswählen

library (shiny)
library(shinyWidgets)
library(viridis)
library(hrbrthemes)
library(rio)

#import
df = import("df.txt"); df

ui <- fluidPage(
  titlePanel(title = h4(" Shiny App", align = "center")),
  sidebarLayout(
    sidebarPanel(
      pickerInput(
        inputId = "lith",
        label = "Wähle Horizont",
        choices = unique(df$Horizont), selected = NULL,
        multiple = TRUE),
      options =  list("max-options" = 1,
                      "max-options-text" = "No more!"),
      br(),
      #tags$h3("Wähle Element"),
      pickerInput(
        inputId = "datatable",
        label = "Wählen Element",
        choices = colnames(df [2:4]),
        #choices = rownames(df),
        multiple = TRUE,
        options = list(`actions-box` = TRUE)
      ),
      textOutput(outputId = "res_classic")),
    
    mainPanel(
      tabsetPanel(type = "tab",
                  tabPanel("Boxplot", plotOutput("box")))
      
    ))
)


server <- function(input, output, session) {
  
  # The boxplot
  output$box = renderPlot({
    ggplot(df, aes(x=input$lith, y=.data[[input$datatable]])) +  
      geom_boxplot(notch = TRUE, color = "#e9ecef", fill="#69b3a2", outlier.colour = "red", width = 0.6)+
      stat_summary(fun = "mean", color = "black", shape = 8) +
      geom_jitter(alpha = 0.8, width = 0.2, height = 0, color = "orange") +
      #facet_wrap( ~ Horizont, scales = 'free_y', nrow = 1)+
      #coord_flip()+
      #ggtitle("Boxplot") +
      labs(title = "Boxplot",
           subtitle = "Black stars: mean value
Red dots: outlier",
           caption = "",
           tag = "")+
      theme_ipsum() +
      theme(axis.title.x=element_blank(),
            axis.text.x=element_blank(),
            axis.ticks.x=element_blank())+
      theme(plot.title = element_text(size=15))
    
  }, res = 96, height = 600, width = 900 )
  
}

shinyApp(ui, server)


Mit Hilfe dieses Codes habe ich nun unter Umgehung der rownames es geschafft, die einzelnen Horizonte zu indizieren. Die geschieht dem "unique" Befehl unter

Code: Alles auswählen

choices = unique(df$Horizont, selected = Null) 

Die zweite Ergänzung ist in ggplot.

Code: Alles auswählen

ggplot(df, aes(x=input$lith, y=.data[[input$datatable]])) 
Hier wurde unter der x-Achse die inputID (input$lith) hinzugefügt. Leider ist immer noch ein Fehler drin! Das Skript läuft durch aber der Plot erstreckt sich immer auf die gesamte Spalte! Bedeutet, der Picker Befehl, zeigt zwar den richtigen Horizont an, der Plot tut dies aber nicht.

Meine Frage lautet. Wie kann ich den korrekten Plot bekommen? Oder andersrum - wie bekomme ich in der Grafik nun tatsächlich nur die Werte für den jeweiligen Horizont angezeigt? Nächste Frage ist es möglich ein zweites Element neben dem ersten aus demselben Horizont dazu zu stellen?

Ich bin jedem Tipp und jeder Hilfe sehr dankbar!

Viele Grüße,

retep
Dateianhänge
df.txt
(4.05 KiB) 83-mal heruntergeladen
retep
Beiträge: 99
Registriert: Do Sep 06, 2018 7:50 pm

Re: entfernen von rowID

Beitrag von retep »

Hallo,

ich gebe nicht auf! Denn ohne diese Funktion brauche ich gar nicht weiter an der App zu arbeiten. Hier ist ein voll reproduzierbares Beispiel meines Problems. Hoffentlich hat jemand nochh eine Idee, wie ich die Fehlermeldung umgehen kann oder vielleicht sogar einen Lösungsvorschlag. Diese Zeilen sollen mein Ziel zeigen. Der Datensatz, welcher erstellt wird, entspricht weitesgehend dem Original! Ziel ist es in shiny die Plots, welche als Egebnis der folgenden Programmzeilen erstellt werden können in Shiny darzustellen

Code: Alles auswählen

library (shiny)
library(shinyWidgets)
library(viridis)
library(hrbrthemes)
library(tidyverse) 



{ set.seed(1234)
   
  A = tibble (
Horizon = ("Horizon A"),
Depth     = seq(0, 2.6, 0.2),
Element_1 =  abs(round(rnorm(14), 2)),
Element_2 =  abs(round(rnorm(14), 2)),
Element_3 =  abs(round(rnorm(14), 2))
); A

 B = tibble (
Horizon = ("Horizon B"),
Depth     = seq(2.8, 43.8, 0.33),
Element_1 =  abs(round(rnorm(125), 2)),
Element_2 =  abs(round(rnorm(125), 2)),
Element_3 =  abs(round(rnorm(125), 2))
); B
 
 C = tibble (
Horizon = ("Horizon C"),
Depth     = seq(44, 50, 0.6),
Element_1 =  abs(round(rnorm(11), 2)),
Element_2 =  abs(round(rnorm(11), 2)),
Element_3 =  abs(round(rnorm(11), 2))
); C
 
 Analyse = rbind (A, B, C); Analyse 
 
 } 
 
 ## single element & single horizon
  Analyse%>%
  pivot_longer(
    cols = Element_1: Element_3,
    names_to = "Element",
    values_to = "Values")%>%
  filter(Horizon == "Horizon B" & Element == "Element_3")%>%
  ggplot(aes(x= "", y= Values)) +  
  geom_boxplot(notch = TRUE, color = "#e9ecef", fill="#69b3a2", outlier.colour = "red", width = 0.6)+
  stat_summary(fun = "mean", color = "black", shape = 8) +
  geom_jitter(alpha = 0.8, width = 0.2, height = 0, color = "orange") +
  #facet_wrap( ~ Element, scales = 'free_y', nrow = 1)+
  labs(title = "Boxplot",
       subtitle = "Black stars: mean value
Red dots: outlier",
       caption = "Horizont C",
       tag = "")+
  theme_ipsum() +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank())+
  theme(plot.title = element_text(size=15))

# compare one element within all horizon`s`
   
  Analyse%>%
    pivot_longer(
      cols = Element_1: Element_3,
      names_to = "Element",
      values_to = "Values")%>%
  filter(Horizon == "Horizon C")%>%
  ggplot(aes(x= "", y= Values)) +  
  geom_boxplot(notch = TRUE, color = "#e9ecef", fill="#69b3a2", outlier.colour = "red", width = 0.6)+
  stat_summary(fun = "mean", color = "black", shape = 8) +
  geom_jitter(alpha = 0.8, width = 0.2, height = 0, color = "orange") +
  facet_wrap( ~ Element, scales = 'free_y', nrow = 1)+
  labs(title = "Boxplot",
       subtitle = "Black stars: mean value
Red dots: outlier",
       caption = "",
       tag = "")+
  theme_ipsum() +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank())+
  theme(plot.title = element_text(size=15))
Als nächstes der Versuch in shiny. Hier der Code:

Code: Alles auswählen

ui <- fluidPage(
  titlePanel(title = h4(" Shiny App", align = "center")),
  sidebarLayout(
    sidebarPanel(
      pickerInput(
        inputId = "lith",
        label = "Select Horizon",
         choices = unique(Analyse.L$Horizon), 
          #l#ist
            #"Horizon" = c("Horizon A", "Horizon B", "Horizon C")),
        selected = NULL,
        multiple = TRUE),
      options =  list("max-options" = 3,
                      "max-options-text" = "No more!"),
      br(),
      #tags$h3("Wähle Element"),
      pickerInput(
        inputId = "element",
        label = "Select Element",
        choices = list(
            "Element" = c("Element_1", "Element_2", "Element_3")),
        selected = NULL,
        #choices = colnames(Analyse.L [3]),
        #choices = rownames(df),
        multiple = TRUE,
        options = list(`actions-box` = TRUE)
      ),
      textOutput(outputId = "res_classic")),
    
    mainPanel(
      tabsetPanel(type = "tab",
                  tabPanel("Boxplot", plotOutput("box")))
      
    ))
)

    server <- shinyServer(function(input, output) {

  
  # The boxplot
  
   reactiveDF <- reactive({return(tbl_df(Analyse.L) %>% 
                                   filter(Element%in% input$element & 
                                            Horizon%in% input$lith))})
    
    #ggplot(Analyse.L, aes(x= "", y= .data[[Analyse.L$Values]])) +  
    #ggplot(aes(x= "", y= .data$Values)) +       
    
      output$box <- renderPlot({reactiveDf(
      ggplot(aes(x= "",  y= .data$Values)) +  
      geom_boxplot(notch = TRUE, color = "#e9ecef", fill="#69b3a2", outlier.colour = "red", width = 0.6)+
      stat_summary(fun = "mean", color = "black", shape = 8) +
      geom_jitter(alpha = 0.8, width = 0.2, height = 0, color = "orange") +
      facet_wrap(~get(input$lith), scales = 'free_y', nrow = 1) +
      #facet_wrap( ~ Element, scales = 'free_y', nrow = 1)+
      labs(title = "Boxplot",
           subtitle = "Black stars: mean value
Red dots: outlier",
           caption = "",
           tag = "")+
      theme_ipsum() +
      theme(axis.title.x=element_blank(),
            axis.text.x=element_blank(),
            axis.ticks.x=element_blank())+
      theme(plot.title = element_text(size=15))
    
  )}, res = 96, height = 600, width = 900 )     
      
  
})

shinyApp(ui, server)
Wenn ich diese Zeilen durchlaufen lassen kommt die Fehlermeldung: Konnte Funktion `reactiveDF`nicht finden (siehe auch Screenshot).

Vielleicht hat jemand eine Idee, ist es ein Namensfehler oder habe ich was übersehen?

Bin über jeden Hinweis & Lösungsvorschlag glücklich!

Vielen Dank,

retep
Dateianhänge
Shiny_App.png
ruedi_br
Beiträge: 159
Registriert: Do Mär 01, 2018 3:53 pm

Re: entfernen von rowID

Beitrag von ruedi_br »

Ich habe zwar noch nie was mit Shiny gemacht, aber der Fehler steckt ja wohl hier:

Code: Alles auswählen

output$box <- renderPlot({reactiveDf(
      ggplot(aes(x= "",  y= .data$Values)) + 
Wenn ich die Hilfe zu renderPlot anschaue, sollte hinter renderPlot eine expression zum generieren des Plots stehen, reactiveDf (weiter oben ist DF groß) soll doch wohl ein Data Frame sein???
HTH
Ruedi
fortune(111)
retep
Beiträge: 99
Registriert: Do Sep 06, 2018 7:50 pm

Re: entfernen von rowID - Shiny

Beitrag von retep »

Hallo ruedi,

ich muss mich entschuldigen! In dem angzeigten code fehlt ein wichtiger Baustein. Nämlich die Transformation des Datensatzes vom 'wide" in das 'long' Format hier reiche ich in den Code nach:

Code: Alles auswählen

library tidyverse
Analyse.L = Analyse%>%
  pivot_longer(
    cols = Element_1: Element_3,
    names_to = "Element",
    values_to = "Values"); Analyse.L
Zu deinem Lösungsansatz:
Wenn ich die Hilfe zu renderPlot anschaue, sollte hinter renderPlot eine expression zum generieren des Plots stehen, reactiveDf (weiter oben ist DF groß) soll doch wohl ein Data Frame sein???
In einem "normalen" ggplot würde das so stehen. Hier wird aber ein reactives Element dem Befehl zum plotten übergeben. Dieses reactive Element ist der gefilterte Datensatz (reactiveDF), welcher in der vorangegangenen Zeile erzeugt wurde. Der Filter besagt, dass der Horizont und das Element gefiltert dem Plotting Befehl übergeben werden. Nur der y-Wert fehlt noch. Diesen Wert reiche ich in ggplot nach.

Das Ziel ist es genau diesselben Plots in Shiny anzeigen zu lassen, welche mit der herkömmlichen Methode in ggplot & facets möglich sind. Nur das ich per click, den Horizont und das Element vorher filtere

Trotzdem fehlt noch ein Baustein, irgendwas sehe ich nicht oder habe ich vergessen - übersehen etc.
Vielleicht hat jemand noch eine Idee wo mein Fauxpas liegt!

Vielen Dank für Eure Hilfe & Geduld,

retep
Benutzeravatar
EDi
Beiträge: 1599
Registriert: Sa Okt 08, 2016 3:39 pm

Re: entfernen von rowID

Beitrag von EDi »

Da ist aber vieles ziemlich unfunktionell,ich weiß gar nicht wo ich anfangen soll...

In der UI Funktion fehlen Klammern, res_classic ist nicht definiert, renderPlot ist auch ein Klammerfehler drin.
Aber der Fehler den du eigentlich suchst, ist wie man reactives anspricht:

Code: Alles auswählen

df <- reactiveDf()
ggplot(data = df, aes(x= "",  y= .data$Values)) +  
...
HIer mal etwas aufgeräumt und grob (da muss noch einiges getan werden, z.b. validierung der inputs was passiert wenn nichts ausgewählt ist?) zum laufen gebracht:

Code: Alles auswählen

library(shiny)
library(shinyWidgets)
library(tidyverse) 

set.seed(1234)
A <-  tibble (
  Horizon = ("Horizon A"),
  Depth     = seq(0, 2.6, 0.2),
  Element_1 =  abs(round(rnorm(14), 2)),
  Element_2 =  abs(round(rnorm(14), 2)),
  Element_3 =  abs(round(rnorm(14), 2))
)
  
B <-  tibble (
  Horizon = ("Horizon B"),
  Depth     = seq(2.8, 43.8, 0.33),
  Element_1 =  abs(round(rnorm(125), 2)),
  Element_2 =  abs(round(rnorm(125), 2)),
  Element_3 =  abs(round(rnorm(125), 2))
)
  
C <-  tibble (
  Horizon = ("Horizon C"),
  Depth     = seq(44, 50, 0.6),
  Element_1 =  abs(round(rnorm(11), 2)),
  Element_2 =  abs(round(rnorm(11), 2)),
  Element_3 =  abs(round(rnorm(11), 2))
)

Analyse = rbind (A, B, C)
Analyse.L = Analyse%>%
  pivot_longer(
    cols = Element_1: Element_3,
    names_to = "Element",
    values_to = "Values")


ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      pickerInput(
        inputId = "lith",
        label = "Select Horizon",
        choices = unique(Analyse.L$Horizon),
        selected = Analyse.L$Horizon[1],
        multiple = TRUE,
        options =  list("max-options" = 3,
                      "max-options-text" = "No more!")),
      br(),
      pickerInput(
        inputId = "element",
        label = "Select Element",
        choices = list(
          "Element" = c("Element_1", "Element_2", "Element_3")),
        selected = "Element_1",
        multiple = TRUE,
        options = list(`actions-box` = TRUE)
      )
    ),
    
    mainPanel(
      tabsetPanel(type = "tab",
                  tabPanel("Boxplot", plotOutput("box")))
      
    ))
)

server <- function(input, output) {

  reactiveDF <- reactive({
    Analyse.L %>% 
      filter(Element %in% input$element & 
        Horizon %in% input$lith)
    })
  
  
  output$box <- renderPlot({
    df <- reactiveDF()
    ggplot(df, aes(x= "",  y= Values)) +  
      geom_boxplot()+
      facet_wrap(vars(input$lith), scales = 'free_y', nrow = 1) 
  })     
}

shinyApp(ui, server)

Tipp: Fang klein an, aber so dass es läuft. Dann verändere Schrittweise und prüfe ob es immernoch läuft.
Wenn es komplexr wird sind tests unerlässlich: https://mastering-shiny.org/scaling-testing.html
Und code immer aufräumen (keine auskommentieren code-stücke, ordentlch eingerückt, style-guide befolgt) - macht das Leben enfacher...
Bitte immer ein reproduzierbares Minimalbeispiel angeben. Meinungen gehören mir und geben nicht die meines Brötchengebers wieder.

Dieser Beitrag ist lizensiert unter einer CC BY 4.0 Lizenz
Bild.
retep
Beiträge: 99
Registriert: Do Sep 06, 2018 7:50 pm

Re: entfernen von rowID

Beitrag von retep »

Hallo Edi,

hab vielen, vielen Dank! Ich habe ja extra mit nur einer Funktion angefangen! Und nicht mehr! Mir war klar, dass ich Schwierigkeiten bekomme. Sind meine ersten Schritte in Shiny!
In der UI Funktion fehlen Klammern, res_classic ist nicht definiert, renderPlot ist auch ein Klammerfehler drin.
Kurioserweise hat mir R keinen Klammerfehler angezeigt! * hmm
Aber der Fehler den du eigentlich suchst, ist wie man reactives anspricht:
Genau dort habe ich gesucht, den Fehler aber nicht gefunden!!!!

Nochmals vielen Dank für deine Hilfe! Bin sehr erleichtert! Der angehängte Link ist extrem hilfreich! Diesen werde ich mir am Wochenende ausführlich zu Gemüte führen.

Viele Grüße,

retep
retep
Beiträge: 99
Registriert: Do Sep 06, 2018 7:50 pm

Re: entfernen von rowID

Beitrag von retep »

Hallo,

vielleicht darf ich nochmal um Hilfe bitten? Ich sehe wieder den Fehler nicht! Dank Edi läuft das Script jetzt durch auch der Einbau in die große App hat perfekt funktioniert. Nun zum nächsten Schritt. Als nächstes wollte ich einen scatter plot einbauen. In der "großen" App funktioniert der Plot! Allerdings benutze ich hier das wide-Format und spreche die x,y Werte direkt über die Spalten an.

Da ich aber umsatteln muss auf das long Format, funktzioniert der scatter plot natürlich nicht mehr. Hier das script:

Code: Alles auswählen

library(shiny)
library(shinyWidgets)
library(tidyverse) 
library(viridis)
library(hrbrthemes)

set.seed(1234)
A <-  tibble (
  Horizon = ("Horizon A"),
  Depth     = seq(0, 2.6, 0.2),
  Element_1 =  abs(round(rnorm(14), 2)),
  Element_2 =  abs(round(rnorm(14), 2)),
  Element_3 =  abs(round(rnorm(14), 2))
)

B <-  tibble (
  Horizon = ("Horizon B"),
  Depth     = seq(2.8, 43.8, 0.33),
  Element_1 =  abs(round(rnorm(125), 2)),
  Element_2 =  abs(round(rnorm(125), 2)),
  Element_3 =  abs(round(rnorm(125), 2))
)

C <-  tibble (
  Horizon = ("Horizon C"),
  Depth     = seq(44, 50, 0.6),
  Element_1 =  abs(round(rnorm(11), 2)),
  Element_2 =  abs(round(rnorm(11), 2)),
  Element_3 =  abs(round(rnorm(11), 2))
)

Analyse = rbind (A, B, C)
Analyse.L = Analyse%>%
  pivot_longer(
    cols = Element_1: Element_3,
    names_to = "Element",
    values_to = "Values")


ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      pickerInput(
        inputId = "lith",
        label = "Select Horizon",
        choices = unique(Analyse.L$Horizon),
        selected = Analyse.L$Horizon[1],
        multiple = TRUE,
        options =  list("max-options" = 3,
                        "max-options-text" = "No more!")),
      br(),
      pickerInput(
        inputId = "element",
        label = "Select X Variable",
        choices = list(
          "Element" = c("Element_1", "Element_2", "Element_3")),
        selected = "Element_1",
        multiple = TRUE,
        options = list(`actions-box` = TRUE)),
      br(),
      pickerInput(
        inputId = "element",
        label = "Select Y Variable",
        choices = list(
          "Element" = c("Element_1", "Element_2", "Element_3")),
        selected = "Element_1",
        multiple = TRUE,
        options = list(`actions-box` = TRUE))
      
    ),
    
    mainPanel(
      tabsetPanel(type = "tab",
                  tabPanel("Scatterplot", plotOutput("plot")))
      
    ))
)

server <- function(input, output, session) { 
  
  reactivescatter <- reactive({
    Analyse.L%>%
      filter(Element %in% input$element& 
             Horizon %in% input$lith)
  })
  
  output$plot <- renderPlot({
    df <- reactivescatter()
    ggplot(df, aes(x = Values, y= Values)) + 
      geom_point() +
      ggtitle("Scatter plot") +
      theme_ipsum() +
      theme(
        plot.title = element_text(size=15)
      )
  }, res = 96)
  
 
}

shinyApp(ui, server)


Wie gesagt, der Plot wird angezeigt. Allerdings wird immer nur ein Element geplottet. Das zweite Element nicht. Also statt xy wird immer nur xx. Ich bekomme keinen Zugriff auf "Y". Hat jemand eine Idee wie ich den scatter plot durchführen kann.

Eine andere Idee wäre vor dem Plot vom long Format zurück in das wide Format zu transformieren. Aber das ist bestimmt Blödsinn!?
Ist es überhaupt möglich aus einem long Format heraus ein x,y Plot zu machen?

Vielen Dank nochmals für Eure Hilfe & Unterstützung,

retep
Benutzeravatar
EDi
Beiträge: 1599
Registriert: Sa Okt 08, 2016 3:39 pm

Re: entfernen von rowID

Beitrag von EDi »

Das zweite Element nicht. Also statt xy wird immer nur xx. Ich bekomme keinen Zugriff auf "Y". Hat jemand eine Idee wie ich den scatter plot durchführen kann.

Code: Alles auswählen

ggplot(df, aes(x = Values, y= Values)) 
Hmm, ggplot macht doch genau das was du ihm sagst: "Bitte auf x und y die Werte der variable 'Values' abbilden".
Bitte immer ein reproduzierbares Minimalbeispiel angeben. Meinungen gehören mir und geben nicht die meines Brötchengebers wieder.

Dieser Beitrag ist lizensiert unter einer CC BY 4.0 Lizenz
Bild.
retep
Beiträge: 99
Registriert: Do Sep 06, 2018 7:50 pm

Re: entfernen von rowID

Beitrag von retep »

Hallo Edi,

Problem ist gelöst. Ich reiche sobald es geht ein script nach. Muss noch ein wenig drüber bügeln bzw. aus zwei scripten eines machen (Scatter & Boxplot). Nur schnell vornweg:

Es ist möglich innerhalb des Shiny scriptes mit zwei verschiedenen Formaten (long/ wide) zu arbeiten. Beide müssen bevor das Shiny script startet definiert werden. Mein Lösung ist nicht ganz so elegant wie in dem Weblink aber die Quelle demonstriert das sehr anschaulich. Auch hier müssen beide Formate vor der Ui (Bedienoberfläche) definiert werden.

https://data.library.virginia.edu/getti ... ith-shiny/

Hier ist die Lösung etwas eleganter. Wobei ich diese noch nicht ganz verstanden habe. Ich habe zumindest beide Formate reingeladen und den Horizont getrennt angesprochen. Etwa so "Horziont" für Scatter und "Horizonte" für Boxplot. Den plot selbst habe ich mit plotly umgesetzt. Funktioniert super!

Nun ja...cool! :D

Jetzt baue ich die nächste Funktion ein..so wie du vorgeschlagen hast, zuerst ein single script. Danach wird alles zusammen gebaut!

BTW: Wäre es denn möglich ein eigenes Unterforum für Shiny einzurichten? Das "Ding" wird immer beliebter, meiner bescheidenen Meinung nach.

Viele Grüße
Benutzeravatar
EDi
Beiträge: 1599
Registriert: Sa Okt 08, 2016 3:39 pm

Re: entfernen von rowID

Beitrag von EDi »

BTW: Wäre es denn möglich ein eigenes Unterforum für Shiny einzurichten? Das "Ding" wird immer beliebter, meiner bescheidenen Meinung nach.
Done :) http://forum.r-statistik.de/viewforum.php?f=27
Bitte immer ein reproduzierbares Minimalbeispiel angeben. Meinungen gehören mir und geben nicht die meines Brötchengebers wieder.

Dieser Beitrag ist lizensiert unter einer CC BY 4.0 Lizenz
Bild.
Antworten