library("tidyverse") # Muchas cosas cool
# Incluidas en tidyverse, en caso de errores cargandolo
library("dplyr") # Manipulación de datos mediante gramatica propia
library("magrittr") # Operadores cañeria (pipes) %>%)
library("readr") # Traducción (pharsing) de archivos a una tibble
library("stringr") # Operadores consistentes para stringsLa función mutate() de dplyr permite crear nuevas variables manteniendo las ya existentes. Por ejemplo, sacando el promedio de dos variables u operaciones similares.
# Importando datos de un CSV
data.location <- 'https://raw.githubusercontent.com/DeepenData/clases/master/06_mutate_pipes_regex/data_oct_08_2020.csv' # Reemplazar por dirección local si bajan el archivo
sim.without.outliers <- read_csv(data.location) # Importa la data a "sim.without.outliers"La gramatica de R utiliza [] y $ para acceder a objetos dentro de una estructura de datos. Con un dataframe como el sacado de un CSV, esto son las filas, columnas, celdas individuales, etc. Dado que un dataframe puede contener dataframes, este modo de acceso puede ir varios niveles de profundidad .
sim.without.outliers$1 saca la primera como vectorsim.without.outliers[1] saca la columna 1sim.without.outliers[ ,c(1,6)] saca las columnas 1 y 6sim.without.outliers[ ,c(1:6)] saca las columnas de 1 a 6sim.without.outliers[ ,-c(1,6)] saca todas las columnas menos la 1 y 6sim.without.outliers[c(10:110) ,c(2:6)] saca las filas 10 a 110 (100 en total) de las columnas 2 a 6sim.without.outliers[[1]] Sacamos la columna 1 como vector## [1] 139729846 403769779 234968788 92103404 313284946 1986643266
## [7] 185451555 96949821 297823265 242665441 196689926 221408800
## [13] 697139689 294169896 466591033 130369358 221895861 193585278
## [19] 323598926 114885079 169819294 296689100 204991230 363420937
## [25] 1273488639 1415642358 751130039 219287099 1563532695 156000468
## [31] 163044979 196698725 937558479 154535089 433849482 265089467
## [37] 176242474 114132862 1759893346 363406125 352426298 1044366175
## [43] 217210021 139208188 61625723 214466955 2319197368 341474397
## [49] 315602657 959714222 231168455 340082205 1702837213 71141852
## [55] 53590128 69835464 849104264 316974795 669475074 239267080
## [61] 138415788 38084241 122917363 707210452 131371803 80951210
## [67] 317049039 387806781 402019145 89318005 42766989 100954015
## [73] 242148670 91210924 177894231 464119826 424034496 890335348
## [79] 474612349 302939270 506484703 1423200472 144241227 185573951
## [85] 226552549 220726226 190740330 134159135 142220681 243602376
## [91] 589350273 306984211 809625162 149428841 108687956 340960733
## [97] 574932184 480901622 238201412 1933132104 236258554 159149151
## [103] 262820305 416437439 1102423653 140017375 182516403 621130490
## [109] 153334103 306507300 479508331 3469022045 146495781 442771648
## [115] 228733701 819689414 299310438 204691347 89564724 281922746
## [121] 1048251440 251089685 337268681 1403280232 310831822 183285718
## [127] 65256307 636942314 353517291 752791078 518799772 699126693
## [133] 380430492 1612209546 169167132 238439583 504001844 153499925
## [139] 203339788 501007077 720071937 514661942 128704058 179853953
## [145] 386816373 56670410 228352427 459598946 504996276 81775682
## [151] 149113060 391979457 289189643 1345879436 151710336 615610679
## [157] 612782783 273949798 210046600 132733280 265878974 260044842
## [163] 607307774 150602719 636874776 212049659 171202362 370767704
## [169] 197661137 174662130 532279756 869478213 476075536 413874167
## [175] 175618194 160299360 227732659 121155779 355542686 177362600
## [181] 294619732 247363565 249091282 964198873 309077761 195145767
## [187] 180927522 745479145 941136589 270529420 115453214
sim.without.outliers['treatment_1']/sim.without.outliers['treatment_2'] %>% log2 -> hola
sim.without.outliers$nueva_columna <- hola # Llena la columna con "hola"
sim.without.outliers$nueva_columna <- NULL # Elimina una columnaPara trabajar con columnas en un dataframe, usamos dplyr::mutate(). Esto crea una nueva columna a partir de existentes, preservando las originales.
# Modificando la tabla con mutate() para calcular el Fold-change (log2/log2)
sim.without.outliers %>% # Tirando data por la cañeria
# Hace un promedio de las columnas TREAT(MENT)
mutate(`Mean treatment` = rowMeans(select(., starts_with("treat")))) %>%
# Hace un promedio de las columnas CONTROL
mutate(`Mean control` = rowMeans(select(., starts_with("control")))) %>%
# Calcula el fold-change de TRATAMIENTO sobre CONTROL
mutate(`log2Ratio` = log2(.[['Mean treatment']] / .[['Mean control']])) -> mutated.simCreamos funciones para evitar escribir miles de lineas de codigo cada vez que queremos repetir algo. Podemos agruparlas y hacer una libreria para proyectos grandes, donde reutilizamos las funciones, o para recursiones.
Es importante considerar que las funciones son para cosas con un comportamiento predecible, e idealmente estable. Para analisis de datos, usualmente tenemos que limpiar los datasets, para tener una estructura “estándar” que nuestras funciones acepten, e idealmente definir un comportamiento de error para las funciones.
# Creamos funciones personalizadas para reutilizar codigo
# Aumenta por el cambio +- fracción del cambio. Es decir 3 +- 0.3 (2.7-3.3)
an.increase <- function(x,change=3,fraction=.1) {
runif(1, min= (change*x - fraction*(change*x)), max=(change*x + fraction*(change*x)))
}
# Disminuye por el cambio +- fracción del cambio. Es decir 0.2 +- 0.1 (0.1-0.3)
a.decrease <- function(x,change=.2,fraction=.1) {
runif(1, min= (change*x - fraction*(change*x )), max=(change*x + fraction*(change*x )))
}Podemos crear una función para el pipeline más complejo que hicimos para el calculo del fold-change.
generate_mean_log2Ratio <- function(x, a.letter){
x %>% mutate(`Mean treatment` = rowMeans(select(., starts_with("treat")))) %>%
mutate(`Mean control` = rowMeans(select(., starts_with("control")))) %>%
mutate(log2Ratio = log2(.[['Mean treatment']] / .[['Mean control']])) %>%
# Les añadiremos un identidicador al final, por """motivos""" (ver luego)
rename_at( vars(matches('^treat|^contr|^mean|log2', ignore.case = TRUE)), funs(paste0(., a.letter)))}# Generando sets para correlaciones
sim.without.outliers %>%
generate_mean_log2Ratio(' A') -> A # Calcula el fold-change inicial sin cambios
sim.without.outliers %>%
mutate(across(starts_with("treatment"), an.increase)) %>%
generate_mean_log2Ratio(' B') -> B # Fold-change aumenta 1.4-1.7 veces
sim.without.outliers %>%
mutate(across(starts_with("treatment"), a.decrease)) %>%
generate_mean_log2Ratio(' C') -> C # Fold-change disminuye -3.3 - -1.7 veces
# Creando el dataset final
cbind(A, # Parte con la data A
select(B, matches('tre|con|log')), # Añade columnas de B
select(C, matches('tre|con|log'))) -> final.data # Añade columnas de C y guarda
final.dataLas funciones de pipes vienen mejor definidas en magrittr.
R no incluye pipes en base.
%>% la pipe tradicional que conocemos y amamos; pasa un obejeto a la entrada de la función%$% permite pasar adicionalmente los nombres del objeto%<>% pipe bidireccional, que sirve para modificar el objeto de la entrada%T% una tee, que permite sacar muestras del pipeline, o derivar a otros pipes## [1] "bg_1" "bg_2" "bg_3" "bg_4" "bg_5" "bg_6"
## [1] 0.7043315
final.data %$% cbind(`log2Ratio A`, `log2Ratio B`, `log2Ratio C`) %>% colSums -> my.col.sum
my.col.sum %>% subset(.>0) -> my.col.sum
my.col.sum %<>% subset(.>0) # Pipe bidireccional util para actualizar cosas
final.data %>% select(matches('log2')) %T>% plot() %>% # %T% permite sacar "muestras" del pipe
select(matches('B|C')) -> my.cols# Esto crea las descripciones del sistema o como estan descritas
# Molestias de los datos ómicos
my.terms <- c('system 1','system 2.7.9','subsystem A','subsystem B and A') # Sistemas super-sistemicos
sample(my.terms, nrow(final.data)/2, replace = T) -> terms.col # Genera una columna de terminos de 1650 lineas .
sample(final.data$name, nrow(final.data)/2, replace = F) -> names.sample # saca 1650 nombres sin duplicados, para una tabla donde habran termiminos para sistemas de identificadores
data.frame(terms.col,names.sample) -> my.info # hacemos una tabla con datos de multiples funciones
inner_join(final.data,my.info, by = c('name'='names.sample'))-> final.data.with.terms # elimina los datos que no tienen los terminos de sistemas que usamos
full_join(final.data,my.info, by = c('name'='names.sample')) -> final.data.with.terms.nas # terminos en que algunos tienen descriptores y otros noLas expresiones regulares, regex, son (generalizadamente) patrones de texto que definen un criterio de busqeda en un string. Por ejemplo, a... se referiria a cualquier string que sea “a” seguido por tres caracteres, como “aaaa”, “a123”, “a”, etc. Diversos lenguajes usan distintas gramaticas de regex, pero en general usan los mismos comodines y expresiones base. R incluye paquetes base que usan regex, pero stringr tiene funciones más detalladas. Sitios como regex101 permiten testear y analizar expresiones regulares.
# Detectando un patron
final.data.with.terms.nas %>% .[['name']] %>%
str_detect('DE') %>% # Detecta si hay o no, tira un vectro booleano
which # Tira una lista de las columnas donde es str_detect es TRUE## [1] 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015
## [16] 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030
## [31] 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045
## [46] 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060
## [61] 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075
## [76] 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090
## [91] 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105
## [106] 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120
## [121] 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135
## [136] 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150
## [151] 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165
## [166] 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180
## [181] 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195
## [196] 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210
## [211] 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225
## [226] 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240
## [241] 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255
## [256] 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270
## [271] 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285
## [286] 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300
## [1] TRUE
## [1] "DE_1" "DE_2" "DE_3" "DE_4" "DE_5" "DE_6" "DE_7" "DE_8"
## [9] "DE_9" "DE_10" "DE_11" "DE_12" "DE_13" "DE_14" "DE_15" "DE_16"
## [17] "DE_17" "DE_18" "DE_19" "DE_20" "DE_21" "DE_22" "DE_23" "DE_24"
## [25] "DE_25" "DE_26" "DE_27" "DE_28" "DE_29" "DE_30" "DE_31" "DE_32"
## [33] "DE_33" "DE_34" "DE_35" "DE_36" "DE_37" "DE_38" "DE_39" "DE_40"
## [41] "DE_41" "DE_42" "DE_43" "DE_44" "DE_45" "DE_46" "DE_47" "DE_48"
## [49] "DE_49" "DE_50" "DE_51" "DE_52" "DE_53" "DE_54" "DE_55" "DE_56"
## [57] "DE_57" "DE_58" "DE_59" "DE_60" "DE_61" "DE_62" "DE_63" "DE_64"
## [65] "DE_65" "DE_66" "DE_67" "DE_68" "DE_69" "DE_70" "DE_71" "DE_72"
## [73] "DE_73" "DE_74" "DE_75" "DE_76" "DE_77" "DE_78" "DE_79" "DE_80"
## [81] "DE_81" "DE_82" "DE_83" "DE_84" "DE_85" "DE_86" "DE_87" "DE_88"
## [89] "DE_89" "DE_90" "DE_91" "DE_92" "DE_93" "DE_94" "DE_95" "DE_96"
## [97] "DE_97" "DE_98" "DE_99" "DE_100" "DE_101" "DE_102" "DE_103" "DE_104"
## [105] "DE_105" "DE_106" "DE_107" "DE_108" "DE_109" "DE_110" "DE_111" "DE_112"
## [113] "DE_113" "DE_114" "DE_115" "DE_116" "DE_117" "DE_118" "DE_119" "DE_120"
## [121] "DE_121" "DE_122" "DE_123" "DE_124" "DE_125" "DE_126" "DE_127" "DE_128"
## [129] "DE_129" "DE_130" "DE_131" "DE_132" "DE_133" "DE_134" "DE_135" "DE_136"
## [137] "DE_137" "DE_138" "DE_139" "DE_140" "DE_141" "DE_142" "DE_143" "DE_144"
## [145] "DE_145" "DE_146" "DE_147" "DE_148" "DE_149" "DE_150" "DE_151" "DE_152"
## [153] "DE_153" "DE_154" "DE_155" "DE_156" "DE_157" "DE_158" "DE_159" "DE_160"
## [161] "DE_161" "DE_162" "DE_163" "DE_164" "DE_165" "DE_166" "DE_167" "DE_168"
## [169] "DE_169" "DE_170" "DE_171" "DE_172" "DE_173" "DE_174" "DE_175" "DE_176"
## [177] "DE_177" "DE_178" "DE_179" "DE_180" "DE_181" "DE_182" "DE_183" "DE_184"
## [185] "DE_185" "DE_186" "DE_187" "DE_188" "DE_189" "DE_190" "DE_191" "DE_192"
## [193] "DE_193" "DE_194" "DE_195" "DE_196" "DE_197" "DE_198" "DE_199" "DE_200"
## [201] "DE_201" "DE_202" "DE_203" "DE_204" "DE_205" "DE_206" "DE_207" "DE_208"
## [209] "DE_209" "DE_210" "DE_211" "DE_212" "DE_213" "DE_214" "DE_215" "DE_216"
## [217] "DE_217" "DE_218" "DE_219" "DE_220" "DE_221" "DE_222" "DE_223" "DE_224"
## [225] "DE_225" "DE_226" "DE_227" "DE_228" "DE_229" "DE_230" "DE_231" "DE_232"
## [233] "DE_233" "DE_234" "DE_235" "DE_236" "DE_237" "DE_238" "DE_239" "DE_240"
## [241] "DE_241" "DE_242" "DE_243" "DE_244" "DE_245" "DE_246" "DE_247" "DE_248"
## [249] "DE_249" "DE_250" "DE_251" "DE_252" "DE_253" "DE_254" "DE_255" "DE_256"
## [257] "DE_257" "DE_258" "DE_259" "DE_260" "DE_261" "DE_262" "DE_263" "DE_264"
## [265] "DE_265" "DE_266" "DE_267" "DE_268" "DE_269" "DE_270" "DE_271" "DE_272"
## [273] "DE_273" "DE_274" "DE_275" "DE_276" "DE_277" "DE_278" "DE_279" "DE_280"
## [281] "DE_281" "DE_282" "DE_283" "DE_284" "DE_285" "DE_286" "DE_287" "DE_288"
## [289] "DE_289" "DE_290" "DE_291" "DE_292" "DE_293" "DE_294" "DE_295" "DE_296"
## [297] "DE_297" "DE_298" "DE_299" "DE_300"
# Detección de patrones algo más avanzada
final.data.with.terms.nas %>% .[['name']] %>% str_extract('[aA-zZ]E.1\\d$') %>% .[!is.na(.)]## [1] "DE_10" "DE_11" "DE_12" "DE_13" "DE_14" "DE_15" "DE_16" "DE_17" "DE_18"
## [10] "DE_19"
## [1] "DE_10" "DE_11" "DE_12" "DE_13" "DE_14" "DE_15" "DE_16" "DE_17"
## [9] "DE_18" "DE_19" "DE_100" "DE_101" "DE_102" "DE_103" "DE_104" "DE_105"
## [17] "DE_106" "DE_107" "DE_108" "DE_109" "DE_110" "DE_111" "DE_112" "DE_113"
## [25] "DE_114" "DE_115" "DE_116" "DE_117" "DE_118" "DE_119" "DE_120" "DE_121"
## [33] "DE_122" "DE_123" "DE_124" "DE_125" "DE_126" "DE_127" "DE_128" "DE_129"
## [41] "DE_130" "DE_131" "DE_132" "DE_133" "DE_134" "DE_135" "DE_136" "DE_137"
## [49] "DE_138" "DE_139" "DE_140" "DE_141" "DE_142" "DE_143" "DE_144" "DE_145"
## [57] "DE_146" "DE_147" "DE_148" "DE_149" "DE_150" "DE_151" "DE_152" "DE_153"
## [65] "DE_154" "DE_155" "DE_156" "DE_157" "DE_158" "DE_159" "DE_160" "DE_161"
## [73] "DE_162" "DE_163" "DE_164" "DE_165" "DE_166" "DE_167" "DE_168" "DE_169"
## [81] "DE_170" "DE_171" "DE_172" "DE_173" "DE_174" "DE_175" "DE_176" "DE_177"
## [89] "DE_178" "DE_179" "DE_180" "DE_181" "DE_182" "DE_183" "DE_184" "DE_185"
## [97] "DE_186" "DE_187" "DE_188" "DE_189" "DE_190" "DE_191" "DE_192" "DE_193"
## [105] "DE_194" "DE_195" "DE_196" "DE_197" "DE_198" "DE_199"
## [1] "DE_100" "DE_101" "DE_102" "DE_103" "DE_104" "DE_105" "DE_106" "DE_107"
## [9] "DE_108" "DE_109" "DE_110" "DE_111" "DE_112" "DE_113" "DE_114" "DE_115"
## [17] "DE_116" "DE_117" "DE_118" "DE_119" "DE_120" "DE_121" "DE_122" "DE_123"
## [25] "DE_124" "DE_125" "DE_126" "DE_127" "DE_128" "DE_129" "DE_130" "DE_131"
## [33] "DE_132" "DE_133" "DE_134" "DE_135" "DE_136" "DE_137" "DE_138" "DE_139"
## [41] "DE_140" "DE_141" "DE_142" "DE_143" "DE_144" "DE_145" "DE_146" "DE_147"
## [49] "DE_148" "DE_149" "DE_150" "DE_151" "DE_152" "DE_153" "DE_154" "DE_155"
## [57] "DE_156" "DE_157" "DE_158" "DE_159" "DE_160" "DE_161" "DE_162" "DE_163"
## [65] "DE_164" "DE_165" "DE_166" "DE_167" "DE_168" "DE_169" "DE_170" "DE_171"
## [73] "DE_172" "DE_173" "DE_174" "DE_175" "DE_176" "DE_177" "DE_178" "DE_179"
## [81] "DE_180" "DE_181" "DE_182" "DE_183" "DE_184" "DE_185" "DE_186" "DE_187"
## [89] "DE_188" "DE_189" "DE_190" "DE_191" "DE_192" "DE_193" "DE_194" "DE_195"
## [97] "DE_196" "DE_197" "DE_198" "DE_199"
final.data.with.terms.nas %>% .[['terms.col']] %>% str_extract('^s.*m') %>%
na.exclude %>% # Es como lo de arriba, pero tira más cosas. Puede ser incomodo
sample(10) %>% unique## [1] "system" "subsystem"
# Reemplazo de patrones (subsystem -> comparment) # TODO: compartment?
final.data.with.terms.nas %>% .[['terms.col']] %>% str_replace('subsyst.*','comparment') %>% na.exclude() %>% sample(10)## [1] "comparment" "comparment" "system 2.7.9" "system 1" "comparment"
## [6] "system 2.7.9" "comparment" "system 1" "comparment" "system 1"
final.data.with.terms.nas %>%
filter(str_detect(name, # en la columna "name"
regex('de.*', # Busca "de*"
ignore_case = T)) & # sin diferencias Minuscula/Mayuscula
str_detect(terms.col, # Busca en la columna "term.cols"
regex('B.*A', ignore_case = F))) # ...terminos que empiezan con B y terminan en Afinal.data.with.terms.nas %>%
filter(str_detect(name, # en la columna "name"
regex('de.*', # ... el patrón "de*"
ignore_case = T)) & # sin diferencias Minuscula/Mayuscula
str_detect(terms.col, # en la columna "term.cols"
regex('\\d.\\d', # ... el patrón "\\d.\\d"
ignore_case = F)) & # sin diferencias Minuscula/Mayuscula
`log2Ratio C` <0) %>% # Y el fold-change es menor a 0
select(matches('log|name|term')) %in% permite detectar si un elemento esta en un objeto. Devuelve TRUE/FALSE.==, >, <= en sentencias if(){}## [1] "ok"
## [1] "ok"
# "system 2.7.9" está en "final.data..." así que TRUE -> 'ok'
final.data.with.terms.nas %>% {
if('system 2.7.9' %in% final.data.with.terms.nas$terms.col) print('ok') else str_extract(.[['terms.col']], 'system.*' )
} %>%
na.omit %>% unique -> unicos## [1] "ok"
R posee formatos propios para guardar datasets, los que tienen la ventaja de conservar propiedades como etiquetas, la estructura, etc; y permitir serializar y comprimir (usando gzip) los datos. Dependiendo de la data, un RDS puede ser 10 veces más pequeño que un CSV.
.RDS son R Data Single, un unico objeto..RData o .Rda, multiples objetos de R.## [1] "06_mutate_pipes_regex.html" "06_mutate_pipes_regex.Rmd"
## [3] "06_mutate_pipes_regex_files" "AA_mis_datos.rds"
## [5] "AA_some_data.RData" "clase_3_AA_oct_08_2020.Rmd"
## [7] "data_oct_08_2020.csv" "final.data.with.terms.nas.rds"
## [9] "rsconnect" "some_data.RData"
## [1] "AA_mis_datos.rds" "AA_some_data.RData"
## [3] "clase_3_AA_oct_08_2020.Rmd"
## [1] "./data_oct_08_2020.csv"
# Save an object to a file
saveRDS(mis.datos, file = "AA_mis_datos.rds")
# Restore the object
hola <- readRDS(file = "AA_mis_datos.rds")
hola[[1]] -> HOLA1
hola[[2]] -> HOLA2
# Save multiple objects
save(HOLA1, HOLA2, file = "AA_some_data.RData")
load("some_data.RData")
unicos## [1] "system 2.7.9" "system A" "system B and A" "system 1"
FONDECYT Postdoctoral Fellow, Universidad de Chile, deepen.data@gmail.com↩︎
Pregrado, Universidad de Chile↩︎