paquetes

library(pheatmap)
library(dendextend)

crear un dataset ficticio

set.seed(1)
mat = matrix(rnorm(200),20,10)
rownames(mat) = paste0("paciente_",1:20)
colnames(mat) = paste0("gen_",1:10)

head(mat)
                gen_1       gen_2      gen_3       gen_4      gen_5       gen_6      gen_7      gen_8
paciente_1 -0.6264538  0.91897737 -0.1645236  2.40161776 -0.5686687 -0.62036668 -0.5059575 -1.9143594
paciente_2  0.1836433  0.78213630 -0.2533617 -0.03924000 -0.1351786  0.04211587  1.3430388  1.1765833
paciente_3 -0.8356286  0.07456498  0.6969634  0.68973936  1.1780870 -0.91092165 -0.2145794 -1.6649724
paciente_4  1.5952808 -1.98935170  0.5566632  0.02800216 -1.5235668  0.15802877 -0.1795565 -0.4635304
paciente_5  0.3295078  0.61982575 -0.6887557 -0.74327321  0.5939462 -0.65458464 -0.1001907 -1.1159201
paciente_6 -0.8204684 -0.05612874 -0.7074952  0.18879230  0.3329504  1.76728727  0.7126663 -0.7508190
                gen_9     gen_10
paciente_1  0.4251004 -1.2313234
paciente_2 -0.2386471  0.9838956
paciente_3  1.0584830  0.2199248
paciente_4  0.8864227 -1.4672500
paciente_5 -0.6192430  0.5210227
paciente_6  2.2061025 -0.1587546

veo el dendograma, y lo guardo

obj = pheatmap(mat,cluster_cols = FALSE, scale = 'row',cutree_rows = 3) # el nro "3" me dice la cantdad de clusters que quiero recuperar
print(obj)

extraigo las categorias (ACA ESTA LA GRACIA)

categorias = cutree(obj$tree_row,3) # ojo, aca elijo tree_row porque lo separe por filas, si lo quisiera separar por columnas, lo haria con obj$tree_col

categorias = data.frame(categorias)
rownames(categorias) = rownames(mat)

categorias
NA
NA

puedo graficar de vuelta, agregando la variable categorica “categoria” para ver si lo hizo bien

categorias$categorias=factor(categorias$categorias) #lo convierto en factor para mejorar el grafico
pheatmap(mat,cluster_cols = FALSE, scale = 'row',cutree_rows = 3,annotation_row=categorias)

unifico los datasets

tabla.clusters=merge(mat, categorias)

#para mirar
tabla.clusters[1:5,]
LS0tDQp0aXRsZTogImNvbW8gcmVjdXBlcmFyIGxhcyBjYXRlZ29yaWFzIGRlbCBkZW5kb2dyYW1hIg0KYXV0aG9yOiAnSnVhbiBCLicgDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBwYXF1ZXRlcw0KDQpgYGB7cn0NCmxpYnJhcnkocGhlYXRtYXApDQpsaWJyYXJ5KGRlbmRleHRlbmQpDQpgYGANCg0KIyMgY3JlYXIgdW4gZGF0YXNldCBmaWN0aWNpbw0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQptYXQgPSBtYXRyaXgocm5vcm0oMjAwKSwyMCwxMCkNCnJvd25hbWVzKG1hdCkgPSBwYXN0ZTAoInBhY2llbnRlXyIsMToyMCkNCmNvbG5hbWVzKG1hdCkgPSBwYXN0ZTAoImdlbl8iLDE6MTApDQoNCmhlYWQobWF0KQ0KDQpgYGANCg0KIyMgdmVvIGVsIGRlbmRvZ3JhbWEsIHkgbG8gZ3VhcmRvDQpgYGB7cn0NCm9iaiA9IHBoZWF0bWFwKG1hdCxjbHVzdGVyX2NvbHMgPSBGQUxTRSwgc2NhbGUgPSAncm93JyxjdXRyZWVfcm93cyA9IDMpICMgZWwgbnJvICIzIiBtZSBkaWNlIGxhIGNhbnRkYWQgZGUgY2x1c3RlcnMgcXVlIHF1aWVybyByZWN1cGVyYXINCnByaW50KG9iaikNCg0KYGBgDQoNCiMjIGV4dHJhaWdvIGxhcyBjYXRlZ29yaWFzIChBQ0EgRVNUQSBMQSBHUkFDSUEpDQpgYGB7cn0NCmNhdGVnb3JpYXMgPSBjdXRyZWUob2JqJHRyZWVfcm93LDMpICMgb2pvLCBhY2EgZWxpam8gdHJlZV9yb3cgcG9ycXVlIGxvIHNlcGFyZSBwb3IgZmlsYXMsIHNpIGxvIHF1aXNpZXJhIHNlcGFyYXIgcG9yIGNvbHVtbmFzLCBsbyBoYXJpYSBjb24gb2JqJHRyZWVfY29sDQoNCmNhdGVnb3JpYXMgPSBkYXRhLmZyYW1lKGNhdGVnb3JpYXMpDQpyb3duYW1lcyhjYXRlZ29yaWFzKSA9IHJvd25hbWVzKG1hdCkNCg0KY2F0ZWdvcmlhcw0KDQoNCmBgYA0KDQojIyBwdWVkbyBncmFmaWNhciBkZSB2dWVsdGEsIGFncmVnYW5kbyBsYSB2YXJpYWJsZSBjYXRlZ29yaWNhICJjYXRlZ29yaWEiIHBhcmEgdmVyIHNpIGxvIGhpem8gYmllbg0KDQpgYGB7cn0NCmNhdGVnb3JpYXMkY2F0ZWdvcmlhcz1mYWN0b3IoY2F0ZWdvcmlhcyRjYXRlZ29yaWFzKSAjbG8gY29udmllcnRvIGVuIGZhY3RvciBwYXJhIG1lam9yYXIgZWwgZ3JhZmljbw0KcGhlYXRtYXAobWF0LGNsdXN0ZXJfY29scyA9IEZBTFNFLCBzY2FsZSA9ICdyb3cnLGN1dHJlZV9yb3dzID0gMyxhbm5vdGF0aW9uX3Jvdz1jYXRlZ29yaWFzKQ0KYGBgDQoNCiMjIHVuaWZpY28gbG9zIGRhdGFzZXRzDQpgYGB7cn0NCnRhYmxhLmNsdXN0ZXJzPW1lcmdlKG1hdCwgY2F0ZWdvcmlhcykNCg0KI3BhcmEgbWlyYXINCnRhYmxhLmNsdXN0ZXJzWzE6NSxdDQpgYGANCg0K