Interpretation der Ausgabe von ctree {partykit} in R.

8

Datengenauigkeiten:

  • Zitat ist eine Dummy-Variable
  • Minuten zählen alle Minuten innerhalb eines Tages
  • Temperatur ist die Temperatur

Hier ist mein Code:

ctree <- ctree(quotation ~ minute + temp, data = visitquot)
print(ctree)

Fitted party:
[1] root
|   [2] minute <= 600
|   |   [3] minute <= 227
|   |   |   [4] temp <= -0.4259
|   |   |   |   [5] temp <= -2.3174: 0.015 (n = 6254, err = 89.7)
|   |   |   |   [6] temp > -2.3174
|   |   |   |   |   [7] minute <= 68: 0.028 (n = 4562, err = 126.3)
|   |   |   |   |   [8] minute > 68: 0.046 (n = 7100, err = 312.8)
|   |   |   [9] temp > -0.4259
|   |   |   |   [10] temp <= 6.0726: 0.015 (n = 56413, err = 860.5)
|   |   |   |   [11] temp > 6.0726: 0.019 (n = 39779, err = 758.9)
|   |   [12] minute > 227
|   |   |   [13] minute <= 501
|   |   |   |   [14] minute <= 291: 0.013 (n = 30671, err = 388.0)
|   |   |   |   [15] minute > 291: 0.009 (n = 559646, err = 5009.3)
|   |   |   [16] minute > 501
|   |   |   |   [17] temp <= 5.2105
|   |   |   |   |   [18] temp <= -1.8393: 0.009 (n = 66326, err = 617.1)
|   |   |   |   |   [19] temp > -1.8393: 0.012 (n = 355986, err = 4289.0)
|   |   |   |   [20] temp > 5.2105
|   |   |   |   |   [21] temp <= 13.6927: 0.014 (n = 287909, err = 3900.7)
|   |   |   |   |   [22] temp > 13.6927
|   |   |   |   |   |   [23] temp <= 14: 0.035 (n = 2769, err = 92.7)
|   |   |   |   |   |   [24] temp > 14: 0.007 (n = 2161, err = 15.9)
|   [25] minute > 600
|   |   [26] temp <= 1.6418
|   |   |   [27] temp <= -2.3366: 0.012 (n = 110810, err = 1268.1)
|   |   |   [28] temp > -2.3366: 0.014 (n = 584457, err = 7973.2)
|   |   [29] temp > 1.6418: 0.016 (n = 3753208, err = 57864.3)

Dann habe ich den Baum geplottet:

plot(ctree, type = "simple")

Und hier ist ein Teil der Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Meine Fragen sind:

  1. print(ctree)Nehmen wir in der ersten Ausgabe von die letzte Zeile [29] temp > 1.6418: 0.016 (n = 3753208, err = 57864.3). Was bedeutet der Wert 0.016? ist das ein p-wert? Und was heißt err = 57864.3das? Es kann keine Zählung von Zuordnungsfehlern sein, da es sich um eine Gleitkommazahl handelt.
  2. Ich konnte nirgendwo eine ähnliche Ausgabe finden, die ich im grauen Quadrat habe. Wenn jemand weiß, wie man es interpretiert. Und wie kann ein p-Wert negativ sein?

Hier gleicher Code aber unterschiedliche Ausgabe

Yohan Obadia
quelle
Ich würde vorschlagen, zunächst denselben Prozess mit partypackage anstelle von zu versuchen partykit. Ich denke, die type='simple'Handlung funktioniert so besser. Was sind die Werte Ihrer Dummy-Variablen? Ist es binär, kategorisch? Ist dies ein Klassifikationsbaum oder ein Regressionsbaum? Wäre gut, eine Zusammenfassung Ihrer 3 Variablen zu sehen. Ich habe das Gefühl, dass Ihre Dummy-Variable (Ausgabevariable) numerisch ist, aber das Modell behandelt sie als Skalierungsvariable und nicht als kategorisch.
AntoniosK
Dann denke ich, dass 0,016 der Durchschnitt der Nullen und Einsen ist. Sie können es als% von 1s (Erfolgsklassen) sehen, aber der richtige Weg, dies zu tun, besteht darin as.character, Faktoren anstelle von Faktoren zu verwenden. Sie müssen das Modell wissen lassen, dass diese Nullen und Einsen Beschriftungen und keine reellen Zahlen sind.
AntoniosK
Die Variable ist numericund quotationbesteht aus 0und 1. Wie kann ich wissen, ob es sich um eine Regression oder einen kategorialen Baum handelt? Ich werde sofort mit quotationals Faktor und dann mit dem partyPaket testen . Was ich jedoch aus Ihrer Antwort verstehe, ist, dass die aktuelle Ausgabe nicht normal ist, oder?
Yohan Obadia
Wenn Ihre Ausgabevariable eine Skalierungsvariable ist, erkennt die Methode sie und erstellt einen Regressionsbaum. Wenn Ihre Ausgabe kategorisch ist, erstellt die Methode einen Klassifizierungsbaum. Es gibt auch einige Hinweise, die auf Ihren errWerten basieren . Wenn es sich um einen Klassifizierungsbaum handelt, handelt es sich um eine Fehlklassifizierung%.
AntoniosK
Wie für das plot(..., type = "simple")Problem. Ich muss noch überprüfen, warum dies derzeit nicht wie gewünscht funktioniert partykit, werde aber versuchen, dies bald zu beheben. Tun Sie in der Zwischenzeit einfach, plot(as.simpleparty(ctree))was den gewünschten Plot erzeugt. (Dies ist besser als zur alten partyImplementierung zurückzukehren ...)
Achim Zeileis

Antworten:

10

Das 0.016, was Sie sehen, ist der Durchschnitt von quotationwhile, errist nur die SSE.

Ich bin partykitaltmodisch und weiß nicht, wie ich das mit dem neuen Paket genau zeigen soll (vielleicht könnte @Achim es veranschaulichen), aber ich kann Ihnen zeigen, wie das mit dem älteren partyPaket gemacht wird.

Erstellen wir also ein reproduzierbares Beispiel

library(partykit)
airq <- subset(airquality, !is.na(Ozone))
airct <- ctree(Ozone ~ ., data = airq)
print(airct)
# Model formula:
#   Ozone ~ Solar.R + Wind + Temp + Month + Day
# 
# Fitted party:
#   [1] root
# |   [2] Temp <= 82
# |   |   [3] Wind <= 6.9: 55.600 (n = 10, err = 21946.4)
# |   |   [4] Wind > 6.9
# |   |   |   [5] Temp <= 77: 18.479 (n = 48, err = 3956.0)
# |   |   |   [6] Temp > 77: 31.143 (n = 21, err = 4620.6)
# |   [7] Temp > 82
# |   |   [8] Wind <= 10.3: 81.633 (n = 30, err = 15119.0)
# |   |   [9] Wind > 10.3: 48.714 (n = 7, err = 1183.4)
# 
# Number of inner nodes:    4
# Number of terminal nodes: 5

Lassen Sie uns nun dtachdas partykitPaket und den gleichen Baum mit partyden gleichen Werten anpassen und berechnen

detach("package:partykit", unload=TRUE)
library(party)
airct <- party::ctree(Ozone ~ ., data = airq)

t(sapply(unique(where(airct)), function(x) {
  n <- nodes(airct, x)[[1]]
  Ozone <- airq[as.logical(n$weights), "Ozone"]
  cbind.data.frame("Node" = as.integer(x), 
                   "n" = length(Ozone), 
                   "Avg."= mean(Ozone), 
                   "Variance"= var(Ozone), 
                   "SSE" = sum((Ozone - mean(Ozone))^2))  
}))

#      Node n  Avg.     Variance SSE     
# [1,] 5    48 18.47917 84.16977 3955.979
# [2,] 3    10 55.6     2438.489 21946.4 
# [3,] 6    21 31.14286 231.0286 4620.571
# [4,] 9    7  48.71429 197.2381 1183.429
# [5,] 8    30 81.63333 521.3437 15118.97

Ich denke, es ist leicht an der Ausgabe zu erkennen, welche welche ist


Der obige Code extrahiert im Grunde genommen die Informationsinformationen Ozoneaus dem angepassten Baum für jeden inneren Knoten und berechnet die relevanten Statistiken


Laut P-values scheint dies eine Art Druckfehler zu sein. Hier ist ein Beispiel, wie Sie das P-values in den inneren Knoten berechnen können

terNodes <- unique(where(airct))
setdiff(1:max(terNodes), terNodes)

sapply(setdiff(1:max(terNodes), terNodes), function(x) {
          n <- nodes(airct, x)[[1]]
          pvalue <- 1 - nodes(airct, x)[[1]]$criterion$maxcriterion
          plab <- ifelse(pvalue < 10^(-3),
                         paste("p <", 10^(-3)),
                         paste("p =", round(pvalue, digits = 3)))
          c("Node" = x, "P-value" = plab)
})

#         [,1]        [,2]        [,3]        [,4]       
# Node    "1"         "2"         "4"         "7"        
# P-value "p < 0.001" "p = 0.002" "p = 0.003" "p = 0.003"

Als Randnotiz: Wenn quotationes sich um eine Dummy-Variable handelt, sollten Sie wahrscheinlich einen Klassifizierungsbaum anpassen (dh in einen Faktor konvertieren), anstatt einen Regressionsbaum wie Sie

David Arenburg
quelle
1
Ich kann Ihre Antwort wegen mangelnden Rufs noch nicht positiv bewerten, aber danke. Ihre Antwort in Kombination mit @Antoniosk hat mir sehr geholfen!
Yohan Obadia
In Bezug auf diesen negativen P-Wert sehen Sie. Es sieht für mich wie ein Fehler aus, da der P-Wert per Definition nicht negativ sein kann. Ich bin sicher, @Achim wird in dieser Angelegenheit etwas hinzufügen können.
David Arenburg
Wie auch immer, fügte auch eine Illustration hinzu, wie man p-Werte in den inneren Knoten berechnet - siehe Bearbeiten.
David Arenburg
Um die knotenspezifischen Zusammenfassungen zu erhalten, besteht die einfachste Lösung (IMHO) darin, nur tapply()die Antwort der entsprechenden Endknoten zu bearbeiten und jede gewünschte Funktion zu berechnen, z tapply(airq$Ozone, predict(airct, type = "node"), function(y) c("n" = length(y), "Avg." = mean(y), "Variance" = var(y), "SSE" = sum((y - mean(y))^2))). Sie können auch eine nachfolgende do.call("rbind", ...)oder eine andere Art der Aggregation verwenden ...
Achim Zeileis
Um die p-Werte zu extrahieren, können Sie diese in der neuen partykitVersion einfach extrahieren . Um die p-Werte aus allen durchgeführten Tests zu erhalten, tun Sie dies einfach library("strucchange")und dann sctest(airct). Daraus können Sie leicht das Minimum oder eine andere Zusammenfassung erhalten, die Sie wünschen. Darüber hinaus können Sie auch nur den minimalen p-Wert extrahieren, der in jedem Knoten (falls vorhanden) gespeichert ist, durch nodeapply(airct, ids = nodeids(airct), FUN = function(n) info_node(n)$p.value).
Achim Zeileis
6

Wie von @DavidArenburg vorgeschlagen, sammle ich meine Kommentare hier in einer anderen Antwort, um die Referenz zu vereinfachen.

(1) Wenn Ihre Antwort binär ist, müssen Sie sie in einen Faktor umwandeln. Andernfalls ist die Schlussfolgerung, die beim Wachsen des Baums verwendet wird, nicht so, wie sie sein sollte, und auch die Vorhersagen, Visualisierungen und Fehlermaßnahmen sind nicht für die Klassifizierung vorgesehen. Weitere Beispiele finden Sie in den Antworten von @DavidArenburg und @AntoniosK. Im Allgemeinen: Außerdem müssen die erklärenden Variablen über geeignete Klassen (numerisch, Faktor, geordneter Faktor) verfügen, damit sie beim Wachsen des Baums korrekt verarbeitet werden können.

(2) Das plot(..., type = "simple")funktioniert derzeit nicht wie gewünscht - mit anderen Worten ist dies ein Fehler. Wir werden das partykitPaket zu gegebener Zeit reparieren . Im Moment können Sie es einfach umgehen, indem Sie es verwenden plot(as.simpleparty(...)). Als reproduzierbares Beispiel:

library("partykit")
data("PimaIndiansDiabetes", package = "mlbench")
ct <- ctree(diabetes ~ ., data = PimaIndiansDiabetes)
plot(as.simpleparty(ct))

einfache Partei

(3) Die $criterionderzeit im type = "simple"Diagramm fälschlicherweise gezeigte Tabelle enthält die Teststatistik und die entsprechenden log 1-p-Werte aus der in jedem Knoten durchgeführten bedingten Inferenz. Das Protokoll anstelle des p-Werts wird verwendet, da es numerisch viel stabiler ist, wenn es für Vergleiche, die Berechnung des Minimalwerts usw. verwendet wird. Beachten Sie, dass die p-Werte bei Signifikanz extrem klein werden können. Betrachten Sie als Beispiel die Testergebnisse, die dem ersten Knoten aus dem obigen Baum entsprechen:

nodeapply(ct, ids = 1, function(n) info_node(n)$criterion)
## $`1`
##                pregnant       glucose   pressure   triceps      insulin
## statistic  3.631413e+00  5.117841e+00  1.1778530  1.455334  2.570457503
## p.value   -6.380290e-09 -2.710725e-37 -0.5937987 -0.313498 -0.002398554
##                    mass      pedigree           age
## statistic  4.185236e+00  3.143294e+00  3.774507e+00
## p.value   -4.181295e-15 -1.180135e-05 -3.262459e-10

(4) Um die tatsächlichen p-Werte (ohne Protokoll) zu extrahieren, gibt es eine Extraktionsfunktion sctest()(für den Strukturänderungstest ), die vom strucchangePaket und auch für die Parameterinstabilitätstests in den mob()Bäumen verwendet wird. Im obigen Beispiel:

library("strucchange")
sctest(ct, node = 1)
##               pregnant  glucose  pressure   triceps     insulin         mass
## statistic 3.776615e+01 166.9745 3.2473947 4.2859164 13.07180346 6.570902e+01
## p.value   6.380290e-09   0.0000 0.4477744 0.2691142  0.00239568 4.218847e-15
##               pedigree          age
## statistic 2.318009e+01 4.357601e+01
## p.value   1.180128e-05 3.262459e-10

Beachten Sie, dass ein p-Wert (Glukose) Null wird, während sein log (1 - p) sehr nahe bei Null liegt, aber nicht ganz. Um nur die minimalen p-Werte aus der ausgewählten Partitionierungsvariablen (falls vorhanden) zu extrahieren, können Sie die nodeapply()Funktion erneut verwenden , um sie von jedem Knoten abzurufen $info:

nodeapply(ct, ids = nodeids(ct), function(n) info_node(n)$p.value)
## $`1`
## glucose 
##       0 
## 
## $`2`
##          age 
## 6.048661e-07 
## 
## $`3`
##        mass 
## 0.001169778 
## 
## ...

(5) Wenn Sie die Antwort in jedem Endknoten zusammenfassen möchten, besteht die einfachste Lösung (IMHO) darin, tapply()die Antwortvariable durch die Knotengruppen einfach zu bearbeiten und eine beliebige Zusammenfassungsfunktion bereitzustellen. Für eine einfachere Übersicht können Sie auch rbind()dies usw. Zum Beispiel:

tab <- tapply(PimaIndiansDiabetes$diabetes, predict(ct, type = "node"),
  function(y) c("n" = length(y), 100 * prop.table(table(y))))
do.call("rbind", tab)
##      n      neg        pos
## 5  144 99.30556  0.6944444
## 6    7 85.71429 14.2857143
## 7  120 82.50000 17.5000000
## 8  214 66.82243 33.1775701
## 11  53 79.24528 20.7547170
## 12 108 39.81481 60.1851852
## 13 122 19.67213 80.3278689
Achim Zeileis
quelle
3

Als Ergänzung zu @DavidArenburgs großartiger Antwort zeige ich Ihnen den Unterschied in den Ausgaben zwischen Regressions- und Klassifizierungsbäumen

library(partykit)

# data
airquality = data.frame(airquality)

# create a numeric binary variable as dependent variable
airquality$OzoneClass = 0
airquality$OzoneClass[airquality$Ozone>=34] =1

# regression tree with scale dependent variable
airq <- subset(airquality, !is.na(Ozone))
airct <- ctree(OzoneClass ~ Temp, data = airq)
print(airct)

# Model formula:
#   OzoneClass ~ Temp
# 
# Fitted party:
#   [1] root
# |   [2] Temp <= 82
# |   |   [3] Temp <= 77: 0.096 (n = 52, err = 4.5)
# |   |   [4] Temp > 77: 0.519 (n = 27, err = 6.7)
# |   [5] Temp > 82: 0.973 (n = 37, err = 1.0)
# 
# Number of inner nodes:    2
# Number of terminal nodes: 3




# create a categorical binary variable as dependent variable
airquality$OzoneClass = 0
airquality$OzoneClass[airquality$Ozone>=34] =1
airquality$OzoneClass = as.factor(airquality$OzoneClass)

# classification tree
airq <- subset(airquality, !is.na(Ozone))
airct <- ctree(OzoneClass ~ Temp, data = airq)
print(airct)

# Model formula:
#   OzoneClass ~ Temp
# 
# Fitted party:
#   [1] root
# |   [2] Temp <= 82
# |   |   [3] Temp <= 77: 0 (n = 52, err = 9.6%)
# |   |   [4] Temp > 77: 1 (n = 27, err = 48.1%)
# |   [5] Temp > 82: 1 (n = 37, err = 2.7%)
# 
# Number of inner nodes:    2
# Number of terminal nodes: 3

Beachten Sie, wie (a) sich die Werte der Bäume von einem Durchschnittswert von 0s und 1s (Regressionsbaum) zu der wahrscheinlichsten Klasse 0/1 (Klassifizierungsbaum) ändern und (b) die errWerte einer Zahl (Fehler) zu einem Prozentsatz werden (Fehlklassifizierung).

AntoniosK
quelle