1150 Stimmen

Gruppierungsfunktionen (tapply, by, aggregate) und die *apply-Familie

Wenn ich in R etwas "map "py machen will, versuche ich normalerweise, eine Funktion in der apply Familie.

Allerdings habe ich die Unterschiede zwischen ihnen nie ganz verstanden - wie { sapply , lapply usw.} die Funktion auf die Eingabe bzw. die gruppierte Eingabe anwenden, wie die Ausgabe aussehen wird oder sogar, was die Eingabe sein kann - also gehe ich sie oft einfach alle durch, bis ich das Gewünschte erhalte.

Kann jemand erklären, wie man wann welchen benutzt?

Mein derzeitiges (wahrscheinlich falsches/unvollständiges) Verständnis ist...

  1. sapply(vec, f) Eingabe ist ein Vektor. Ausgabe ist ein Vektor/Matrix, wobei Element i es f(vec[i]) und gibt Ihnen eine Matrix, wenn f hat einen Multi-Element-Ausgang

  2. lapply(vec, f) : gleich wie sapply , aber die Ausgabe ist eine Liste?

  3. apply(matrix, 1/2, f) Eingabe ist eine Matrix. Ausgabe ist ein Vektor, dessen Element i ist f(Zeile/Spalte i der Matrix)

  4. tapply(vector, grouping, f) Ausgabe ist eine Matrix/ein Array, wobei ein Element in der Matrix/dem Array der Wert von f bei einer Gruppierung g des Vektors, und g wird an die Zeilen-/Spaltennamen angehängt

  5. by(dataframe, grouping, f) : l g eine Gruppierung sein. anwenden f zu jeder Spalte der Gruppe/des Datenrahmens. Die Gruppierung und der Wert von f an jeder Spalte.

  6. aggregate(matrix, grouping, f) : ähnlich wie by , aber anstatt die Ausgabe hübsch auszudrucken, packt Aggregate alles in einen Datenrahmen.

Nebenfrage: Ich habe immer noch nicht gelernt, plyr oder reshape - würde plyr o reshape alle diese Produkte vollständig ersetzen?

1438voto

joran Punkte 163857

R hat viele *apply-Funktionen, die in den Hilfedateien gut beschrieben sind (z.B. ?apply ). Es gibt jedoch so viele davon, dass es für Anfänger schwierig sein kann, zu entscheiden, welche Funktion für ihre Situation geeignet ist, oder sich sogar an alle zu erinnern. Sie haben vielleicht ein allgemeines Gefühl dafür, dass "ich hier eine *Anwendungsfunktion verwenden sollte", aber es kann anfangs schwierig sein, sie alle auseinanderzuhalten.

Trotz der (in anderen Antworten erwähnten) Tatsache, dass ein Großteil der Funktionalität der *apply-Familie durch das äußerst beliebte plyr Pakets bleiben die Basisfunktionen nützlich und wissenswert.

Diese Antwort soll als eine Art Leitfaden dienen 道しるべ für neue Benutzer, um ihnen zu helfen, die richtige *Anwendungsfunktion für ihr spezielles Problem zu finden. N no Es ist nicht beabsichtigt, die R-Dokumentation einfach wiederzukäuen oder zu ersetzen! Ich hoffe, dass diese Antwort Ihnen hilft, zu entscheiden, welche *Anwendungsfunktion für Ihre Situation geeignet ist, und es dann an Ihnen liegt, sie weiter zu erforschen. Mit einer Ausnahme wird nicht auf Leistungsunterschiede eingegangen.

  • 反映させる - Wenn Sie eine Funktion auf die Zeilen oder Spalten anwenden wollen einer Matrix (und höherdimensionaler Analoga) anwenden wollen; für Datenrahmen ist dies im Allgemeinen nicht ratsam, da die Funktion zunächst auf eine Matrix angewendet wird.

     # Two dimensional matrix
     M <- matrix(seq(1,16), 4, 4)
    
     # apply min to rows
     apply(M, 1, min)
     [1] 1 2 3 4
    
     # apply max to columns
     apply(M, 2, max)
     [1]  4  8 12 16
    
     # 3 dimensional array
     M <- array( seq(32), dim = c(4,4,2))
    
     # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension
     apply(M, 1, sum)
     # Result is one-dimensional
     [1] 120 128 136 144
    
     # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension
     apply(M, c(1,2), sum)
     # Result is two-dimensional
          [,1] [,2] [,3] [,4]
     [1,]   18   26   34   42
     [2,]   20   28   36   44
     [3,]   22   30   38   46
     [4,]   24   32   40   48

    Wenn Sie Zeilen-/Spaltenmittelwerte oder Summen für eine 2D-Matrix benötigen, müssen Sie die hoch optimierte, blitzschnelle colMeans , rowMeans , colSums , rowSums .

  • 鳧を付ける - Wenn Sie eine Funktion auf jedes Element einer Liste anwenden und eine Liste zurückerhalten.

    Dies ist das Arbeitspferd für viele der anderen *Anwendungsfunktionen. Schälen Sie ihren Code und Sie werden oft finden lapply の下にあります。

     x <- list(a = 1, b = 1:3, c = 10:100) 
     lapply(x, FUN = length) 
     $a 
     [1] 1
     $b 
     [1] 3
     $c 
     [1] 91
     lapply(x, FUN = sum) 
     $a 
     [1] 1
     $b 
     [1] 6
     $c 
     [1] 5005
  • サップル - Wenn Sie eine Funktion auf jedes Element einer Liste anwenden wollen, aber eine ベクトル zurück, sondern eine Liste.

    Wenn Sie sich bei der Eingabe von unlist(lapply(...)) innehalten und überlegen sapply .

     x <- list(a = 1, b = 1:3, c = 10:100)
     # Compare with above; a named vector, not a list 
     sapply(x, FUN = length)  
     a  b  c   
     1  3 91
    
     sapply(x, FUN = sum)   
     a    b    c    
     1    6 5005 

    Bei fortgeschrittenen Anwendungen von sapply wird sie versuchen, die Ergebnis in ein mehrdimensionales Array zu überführen, falls dies sinnvoll ist. Zum Beispiel, wenn unsere Funktion Vektoren der gleichen Länge zurückgibt, sapply werden sie als Spalten einer Matrix verwendet:

     sapply(1:5,function(x) rnorm(3,x))

    Wenn unsere Funktion eine 2-dimensionale Matrix zurückgibt, sapply tut im Wesentlichen das Gleiche, wobei jede zurückgegebene Matrix als ein einziger langer Vektor behandelt wird:

     sapply(1:5,function(x) matrix(x,2,2))

    指定しない限り simplify = "array" in diesem Fall werden die einzelnen Matrizen verwendet, um ein mehrdimensionales Array zu bilden:

     sapply(1:5,function(x) matrix(x,2,2), simplify = "array")

    Jedes dieser Verhaltensweisen ist natürlich davon abhängig, dass unsere Funktion Vektoren oder Matrizen derselben Länge oder Dimension zurückgibt.

  • 生える - を使いたいとき sapply aber vielleicht müssen etwas mehr Geschwindigkeit aus Ihrem Code herausholen oder 安全性を高める .

    について vapply geben Sie R im Grunde ein Beispiel dafür, welche Art von Dingen Ihre Funktion zurückgeben wird, was einige Zeit sparen kann, um die zurückgegebenen Werte in einen einzelnen atomaren Vektor einzupassen.

     x <- list(a = 1, b = 1:3, c = 10:100)
     #Note that since the advantage here is mainly speed, this
     # example is only for illustration. We're telling R that
     # everything returned by length() should be an integer of 
     # length 1. 
     vapply(x, FUN = length, FUN.VALUE = 0L) 
     a  b  c  
     1  3 91
  • 充てがう - Wenn Sie mehrere Datenstrukturen haben (z. B. Vektoren, Listen) und Sie wollen eine Funktion auf das erste Element jeder Struktur anwenden der einzelnen Elemente und dann auf die 2. Elemente der einzelnen Elemente usw. anwenden möchten, muss das Ergebnis in einen Vektor/Array wie in sapply .

    Dies ist multivariat in dem Sinne, dass Ihre Funktion Folgendes akzeptieren muss mehrere Argumente akzeptieren muss.

     #Sums the 1st elements, the 2nd elements, etc. 
     mapply(sum, 1:5, 1:5, 1:5) 
     [1]  3  6  9 12 15
     #To do rep(1,4), rep(2,3), etc.
     mapply(rep, 1:4, 4:1)   
     [[1]]
     [1] 1 1 1 1
    
     [[2]]
     [1] 2 2 2
    
     [[3]]
     [1] 3 3
    
     [[4]]
     [1] 4
  • 地図 - のラッパーです。 mapply con SIMPLIFY = FALSE zurück, so dass garantiert eine Liste zurückgegeben wird.

     Map(sum, 1:5, 1:5, 1:5)
     [[1]]
     [1] 3
    
     [[2]]
     [1] 6
    
     [[3]]
     [1] 9
    
     [[4]]
     [1] 12
    
     [[5]]
     [1] 15
  • 打っ手繰る - Denn wenn Sie eine Funktion auf jedes Element einer 入れ子リスト Struktur, rekursiv.

    Um Ihnen eine Vorstellung davon zu geben, wie ungewöhnlich rapply ist, dass ich es vergessen habe, als ich diese Antwort zuerst geschrieben habe! Natürlich bin ich sicher, dass viele Menschen es verwenden, aber YMMV. rapply wird am besten mit einer benutzerdefinierten Funktion veranschaulicht, die anzuwenden ist:

     # Append ! to string, otherwise increment
     myFun <- function(x){
         if(is.character(x)){
           return(paste(x,"!",sep=""))
         }
         else{
           return(x + 1)
         }
     }
    
     #A nested list structure
     l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), 
               b = 3, c = "Yikes", 
               d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5)))
    
     # Result is named vector, coerced to character          
     rapply(l, myFun)
    
     # Result is a nested list like l, with values altered
     rapply(l, myFun, how="replace")
  • タップリ - Denn wenn Sie eine Funktion auf 部分集合 eines Vektors, und die Teilmengen werden durch einen anderen Vektor definiert, in der Regel einen Faktor.

    Das schwarze Schaf der *apply-Familie, gewissermaßen. Der Gebrauch der Hilfedatei von Ausdrucks "ragged array" kann ein wenig 紛らわしい aber eigentlich ist es ganz einfach.

    ベクトルです。

     x <- 1:20

    Ein Faktor (von gleicher Länge!), der Gruppen definiert:

     y <- factor(rep(letters[1:5], each = 4))

    Addieren Sie die Werte in x innerhalb jeder Untergruppe definiert durch y :

     tapply(x, y, sum)  
      a  b  c  d  e  
     10 26 42 58 74 

    Komplexere Beispiele können behandelt werden, wenn die Untergruppen wie folgt definiert sind durch die eindeutigen Kombinationen einer Liste von mehreren Faktoren definiert sind. tapply ist ähnlich wie die Split-Apply-Combine-Funktionen, die in R üblich sind die in R üblich sind ( aggregate , by , ave , ddply usw.) Daher sein Status als schwarzes Schaf.

212voto

JoFrhwld Punkte 8637

Nebenbei bemerkt, hier ist, wie die verschiedenen plyr Funktionen entsprechen der Basis *apply Funktionen (aus dem Dokument Einführung in plyr von der plyr-Webseite http://had.co.nz/plyr/ )

Base function   Input   Output   plyr function 
---------------------------------------
aggregate        d       d       ddply + colwise 
apply            a       a/l     aaply / alply 
by               d       l       dlply 
lapply           l       l       llply  
mapply           a       a/l     maply / mlply 
replicate        r       a/l     raply / rlply 
sapply           l       a       laply 

の目標のひとつは plyr ist es, einheitliche Namenskonventionen für jede der Funktionen zu schaffen, indem die Eingabe- und Ausgabedatentypen im Funktionsnamen kodiert werden. Es sorgt auch für Konsistenz bei der Ausgabe, indem die Ausgabe von dlply() ist leicht übertragbar auf ldply() um nützliche Ergebnisse zu erzielen, usw.

Konzeptionell, Lernen plyr ist nicht schwieriger als das Verständnis der Basis *apply の機能を持つ。

plyr y reshape Funktionen haben fast alle diese Funktionen in meinem täglichen Gebrauch ersetzt. Aber auch aus dem Dokument "Einführung in Plyr":

関連機能 tapply y sweep haben keine entsprechende Funktion in plyr und nützlich bleiben. merge ist nützlich, um Zusammenfassungen mit den Originaldaten zu kombinieren.

149voto

isomorphismes Punkte 7955

Aus Folie 21 von http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :

apply, sapply, lapply, by, aggregate

(Es ist hoffentlich klar, dass apply entspricht dem von @Hadley aaply y aggregate entspricht dem von @Hadley ddply usw. Folie 20 auf der gleichen Slideshare-Webseite wird dies verdeutlichen, falls Sie es nicht von diesem Bild aus verstehen).

(links ist die Eingabe, oben die Ausgabe)

118voto

Assad Ebrahim Punkte 5998

まず始めに Jorans ausgezeichnete Antwort -- Es ist fraglich, ob das noch zu überbieten ist.

Die folgenden Gedächtnisstützen können Ihnen helfen, sich die Unterschiede zwischen den einzelnen Kategorien zu merken. Während einige offensichtlich sind, sind andere vielleicht weniger offensichtlich --- für diese werden Sie in Jorans Diskussionen eine Begründung finden.

ニーモニック

  • lapply es un リスト apply, die auf eine Liste oder einen Vektor wirkt und eine Liste zurückgibt.
  • sapply es un 平易 lapply (die Funktion gibt standardmäßig einen Vektor oder eine Matrix zurück, wenn möglich)
  • vapply es un 確認済み適用 (ermöglicht die Vorgabe des Typs des Rückgabeobjekts)
  • rapply es un 再帰的 gelten für verschachtelte Listen, d. h. Listen in Listen
  • tapply es un タグ付き anwenden, wobei die Tags die Teilmengen identifizieren
  • apply es ジェネリック Funktion: wendet eine Funktion auf die Zeilen oder Spalten einer Matrix an (oder, allgemeiner, auf die Dimensionen eines Arrays)

正しい背景の構築

を使用する場合 apply Familie immer noch ein wenig fremd erscheint, dann könnte es sein, dass Sie einen wichtigen Gesichtspunkt übersehen.

Diese beiden Artikel können helfen. Sie liefern den notwendigen Hintergrund für die Motivation der Techniken der funktionalen Programmierung die von der Kommission zur Verfügung gestellt werden apply Familie von Funktionen.

Benutzer von Lisp werden das Paradigma sofort erkennen. Wenn Sie mit Lisp nicht vertraut sind, werden Sie, sobald Sie sich mit FP vertraut gemacht haben, einen leistungsfähigen Standpunkt für die Verwendung in R gewonnen haben -- und apply viel mehr Sinn machen.

64voto

SabDeM Punkte 6858

Da ich festgestellt habe, dass die (sehr guten) Antworten in diesem Beitrag nicht by y aggregate Erklärungen. Hier ist mein Beitrag.

バイ

El by Funktion, wie in der Dokumentation angegeben, kann jedoch als "Wrapper" für tapply . の威力 by ergibt sich, wenn wir eine Aufgabe berechnen wollen, die tapply nicht bewältigen kann. Ein Beispiel ist dieser Code:

ct <- tapply(iris$Sepal.Width , iris$Species , summary )
cb <- by(iris$Sepal.Width , iris$Species , summary )

 cb
iris$Species: setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 
-------------------------------------------------------------- 
iris$Species: versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 
-------------------------------------------------------------- 
iris$Species: virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 

ct
$setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 

$versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 

$virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 

Wenn wir diese beiden Objekte drucken, ct y cb haben wir "im Wesentlichen" die gleichen Ergebnisse, die sich nur in der Art der Darstellung und den unterschiedlichen class 属性がそれぞれ by para cb y array para ct .

やはり、その力は by entsteht, wenn wir nicht mit tapply Der folgende Code ist ein Beispiel:

 tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) : 
  arguments must have same length

R sagt, dass die Argumente gleich lang sein müssen, z.B. "wir wollen die summary aller Variablen in iris 係数にそって Species "aber R kann das einfach nicht, weil es nicht weiß, wie es damit umgehen soll.

とともに by Funktion R eine bestimmte Methode für data frame クラスを作成し summary Funktion funktioniert auch dann, wenn die Länge des ersten Arguments (und auch der Typ) unterschiedlich sind.

bywork <- by(iris, iris$Species, summary )

bywork
iris$Species: setosa
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200   versicolor: 0  
 Median :5.000   Median :3.400   Median :1.500   Median :0.200   virginica : 0  
 Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246                  
 3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300                  
 Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600                  
-------------------------------------------------------------- 
iris$Species: versicolor
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  
-------------------------------------------------------------- 
iris$Species: virginica
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
 Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026                  
 3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300                  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500     

es funktioniert tatsächlich und das Ergebnis ist sehr überraschend. Es ist ein Objekt der Klasse by 沿って Species (etwa für jeden von ihnen) berechnet die summary 各変数の

なお、第1引数が data frame muss die ausgeführte Funktion eine Methode für diese Objektklasse haben. Zum Beispiel verwenden wir diesen Code mit der mean Funktion haben wir diesen Code, der überhaupt keinen Sinn hat:

 by(iris, iris$Species, mean)
iris$Species: setosa
[1] NA
------------------------------------------- 
iris$Species: versicolor
[1] NA
------------------------------------------- 
iris$Species: virginica
[1] NA
Warning messages:
1: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
2: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
3: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA

AGGREGATE

aggregate kann als eine andere Art der Nutzung angesehen werden tapply wenn wir sie auf diese Weise verwenden.

at <- tapply(iris$Sepal.Length , iris$Species , mean)
ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)

 at
    setosa versicolor  virginica 
     5.006      5.936      6.588 
 ag
     Group.1     x
1     setosa 5.006
2 versicolor 5.936
3  virginica 6.588

Die beiden unmittelbaren Unterschiede sind, dass das zweite Argument von aggregate なければならない 並べられる tapply puede (nicht obligatorisch) eine Liste ist und dass die Ausgabe von aggregate ein Datenrahmen ist, während derjenige von tapplyarray .

の力を発揮します。 aggregate ist, dass es leicht Teilmengen der Daten verarbeiten kann mit subset Argument und hat Methoden für ts オブジェクトと formula もあります。

Diese Elemente machen aggregate leichter zu bearbeiten als tapply in einigen Situationen. Hier sind einige Beispiele (in der Dokumentation verfügbar):

ag <- aggregate(len ~ ., data = ToothGrowth, mean)

 ag
  supp dose   len
1   OJ  0.5 13.23
2   VC  0.5  7.98
3   OJ  1.0 22.70
4   VC  1.0 16.77
5   OJ  2.0 26.06
6   VC  2.0 26.14

Das Gleiche können wir erreichen mit tapply aber die Syntax ist etwas schwieriger und die Ausgabe (unter bestimmten Umständen) weniger lesbar:

att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)

 att
       OJ    VC
0.5 13.23  7.98
1   22.70 16.77
2   26.06 26.14

Es gibt andere Zeiten, in denen wir nicht mit by o tapply und wir müssen die aggregate .

 ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)

 ag1
  Month    Ozone     Temp
1     5 23.61538 66.73077
2     6 29.44444 78.22222
3     7 59.11538 83.88462
4     8 59.96154 83.96154
5     9 31.44828 76.89655

Wir können das vorherige Ergebnis nicht erhalten mit tapply in einem einzigen Aufruf, aber wir müssen den Mittelwert entlang Month für jedes Element und kombinieren sie dann (beachten Sie auch, dass wir die na.rm = TRUE は、そのためです。 formula Methoden der aggregate Funktion hat standardmäßig die na.action = na.omit ):

ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)

 cbind(ta1, ta2)
       ta1      ta2
5 23.61538 65.54839
6 29.44444 79.10000
7 59.11538 83.90323
8 59.96154 83.96774
9 31.44828 76.90000

と同時に by können wir nicht erreichen, dass der folgende Funktionsaufruf tatsächlich einen Fehler zurückgibt (der aber höchstwahrscheinlich mit der bereitgestellten Funktion zusammenhängt, mean ):

by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)

In anderen Fällen sind die Ergebnisse die gleichen, und die Unterschiede liegen nur in der Klasse (und dann, wie sie angezeigt/gedruckt wird und nicht nur - Beispiel, wie man sie unterteilt) Objekt:

byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)

Der vorherige Code erreicht das gleiche Ziel und die gleichen Ergebnisse, an manchen Stellen ist es nur eine Frage des persönlichen Geschmacks und der Bedürfnisse, welches Werkzeug man benutzt; die beiden vorherigen Objekte haben sehr unterschiedliche Bedürfnisse in Bezug auf die Unterteilung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X