7.8: Trabajar con Texto
- Page ID
- 151615
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)A veces tu conjunto de datos es bastante pesado de texto. Esto puede ser por muchas razones diferentes. Tal vez los datos brutos en realidad se toman de fuentes de texto (por ejemplo, artículos de periódicos), o tal vez su conjunto de datos contenga muchas respuestas gratuitas a preguntas de encuestas, en las que las personas pueden escribir cualquier texto que les guste en respuesta a alguna consulta. O tal vez solo necesitas rejig parte del texto utilizado para describir variables de escala nominal. Independientemente de cuál sea la razón, probablemente querrás saber un poco sobre cómo manejar el texto en R. Algunas cosas que ya sabes hacer: He discutido el uso de nchar ()
para calcular el número de caracteres en una cadena (Sección 3.8.1), y muchas de las herramientas de propósito general que tengo discutidos en otra parte (por ejemplo, el operador ==
) se han aplicado tanto a datos de texto como a datos numéricos. Sin embargo, debido a que los datos de texto son bastante ricos, y generalmente no están tan bien estructurados como los datos numéricos, R proporciona muchas herramientas adicionales que son bastante específicas para el texto. En esta sección discuto solo aquellas herramientas que vienen como parte de los paquetes base, pero hay otras posibilidades por ahí: el paquete stringr
proporciona una alternativa poderosa que es mucho más coherente que las herramientas básicas, y bien vale la pena indagar.
Acortamiento de una cuerda
La primera tarea de la que quiero hablar es cómo acortar una cadena de caracteres. Por ejemplo, supongamos que tengo un vector que contiene los nombres de varios animales diferentes:
animals <- c( "cat", "dog", "kangaroo", "whale" )
Podría ser útil en algunos contextos extraer las tres primeras letras de cada palabra. Esto suele ser útil al anotar figuras, o al crear etiquetas variables: a menudo es muy inconveniente usar el nombre completo, por lo que quieres acortarlo a un código corto por razones de espacio. La función strtrim ()
se puede utilizar para este propósito. Tiene dos argumentos: x
es un vector que contiene el texto a acortar y width
especifica el número de caracteres a mantener. Cuando se aplica a los datos de los animales
, esto es lo que obtenemos:
strtrim( x = animals, width = 3 )
## [1] "cat" "dog" "kan" "wha"
Tenga en cuenta que lo único que hace strtrim ()
es cortar el exceso de caracteres al final de una cadena. No inserta ningún carácter de espacio en blanco para rellenarlos si la cadena original es más corta que el argumento width
. Por ejemplo, si recorto los datos de los animales
a 4 caracteres, esto es lo que obtengo:
strtrim( x = animals, width = 4 )
## [1] "cat" "dog" "kang" "whal"
Las cuerdas “cat”
y “dog”
siguen usando solo 3 caracteres. Bien, pero ¿y si no quieres empezar desde la primera letra? Supongamos, por ejemplo, que sólo quería quedarme con la segunda y tercera letra de cada palabra. Eso no sucede con tanta frecuencia, pero hay algunas situaciones en las que hay que hacer algo así. Si eso sucede, entonces la función que necesitas es substr ()
, en la que especificas un punto de inicio
y un punto de parada
en lugar de especificar el ancho. Por ejemplo, para conservar solo las letras 2 y 3 de los diversos animales
, puedo hacer lo siguiente:
substr( x = animals, start = 2, stop = 3 )
## [1] "at" "og" "an" "ha"
Pega cuerdas juntas
Mucho más comúnmente, necesitarás pegar varias cadenas de caracteres juntas o separarlas. Para pegar varias cuerdas juntas, la función paste ()
es muy útil. Hay tres argumentos para la función paste ()
:
...
Como es habitual, los puntos “coinciden” contra cualquier número de entradas. En este caso, las entradas deben ser las distintas cadenas que quieras pegar juntas.sep
. Este argumento debe ser una cadena, indicando qué caracteres R debe usar como separadores, para mantener cada una de las cadenas originales separadas entre sí en la salida pegada. Por defecto el valor es un solo espacio,sep = ""
. Esto se hace un poco más claro cuando miramos los ejemplos.colapso
. Este es un argumento que indica si la funciónpaste ()
debe interpretar las entradas vectoriales como cosas a colapsar, o si un vector de entradas debe convertirse en un vector de salidas. El valor predeterminado escollapse = NULL
que se interpreta como que significa que los vectores no deben colapsarse. Si desea contraer vectores en una sola cadena, entonces debe especificar un valor paracontraer
. Específicamente, el valor decolapso
debe corresponder al carácter separador que desea utilizar para las entradas colapsadas. Nuevamente, vea los ejemplos a continuación para obtener más detalles.
Eso probablemente aún no tenga mucho sentido, así que comencemos con un ejemplo sencillo. Primero, intentemos pegar dos palabras juntas, así:
paste( "hello", "world" )
## [1] "hello world"
Observe que R ha insertado un espacio entre el “hola”
y el “mundo”
. Supongamos que eso no es lo que yo quería. En cambio, podría querer usar.
como el carácter separador, o para no usar ningún separador en absoluto. Para hacer cualquiera de esos, necesitaría especificar sep = “.”
o sep = “”
. 121 Por ejemplo:
paste( "hello", "world", sep = "." )
## [1] "hello.world"
Ahora consideremos un ejemplo un poco más complicado. Supongamos que tengo dos vectores que quiero pegar ()
juntos. Digamos algo como esto:
hw <- c( "hello", "world" )
ng <- c( "nasty", "government" )
Y supongamos que quiero pegarlas juntas. No obstante, si lo piensas, esta afirmación es algo ambigua. Podría significar que quiero hacer una pasta “sabia de elementos”, en la que se peguen todos los primeros elementos (“hola desagradable”
) y se peguen todos los segundos elementos (“gobierno mundial”
). O, alternativamente, podría tener la intención de colapsar todo en una gran cadena (“hola desagradable gobierno mundial”
). Por defecto, la función paste ()
asume que quieres hacer una pasta por elementos:
paste( hw, ng )
## [1] "hello nasty" "world government"
Sin embargo, no hay nada que le imponga anular este valor predeterminado. Todo lo que tienes que hacer es especificar un valor para el argumento collapse
, y R tirará todo en una cadena grande y sucia. Para darte una idea de cómo funciona exactamente esto, lo que haré en este siguiente ejemplo es especificar diferentes valores para sep
y collapse
:
paste( hw, ng, sep = ".", collapse = ":::")
## [1] "hello.nasty:::world.government"
Dividir cadenas
En otras ocasiones tienes el problema opuesto al de la última sección: tienes una gran cantidad de texto agrupado en una sola cadena que necesita ser separada y almacenada como varias variables diferentes. Por ejemplo, el conjunto de datos que te envían podría incluir una sola variable que contenga el nombre completo de alguien, y necesitas separarlo en nombres y apellidos. Para hacer esto en R puedes usar la función strsplit ()
, y por el bien del argumento, supongamos que la cadena que quieres dividir es la siguiente cadena:
monkey <- "It was the best of times. It was the blurst of times."
Para usar la función strsplit ()
para romper esto, hay tres argumentos a los que debes prestar especial atención:
x
. Un vector de cadenas de caracteres que contiene los datos que desea dividir.dividir
. Dependiendo del valor del argumentofijo
, se trata de una cadena fija que especifica un delimitador, o una expresión regular que coincide con uno o más delimitadores posibles. Si no sabes qué son las expresiones regulares (probablemente la mayoría de los lectores de este libro), no uses esta opción. Simplemente especifique una cadena separadora, tal como lo haría para la funciónpaste ()
.fijo
. Establecerfijo = VERDADERO
si desea utilizar un delimitador fijo. Como se señaló anteriormente, a menos que entiendas expresiones regulares esto es definitivamente lo que quiere. Sin embargo, el valor predeterminado esfijo = FALSO
, por lo que hay que establecerlo explícitamente.
Veamos un ejemplo sencillo:
monkey.1 <- strsplit( x = monkey, split = " ", fixed = TRUE )
monkey.1
## [[1]]
## [1] "It" "was" "the" "best" "of" "times." "It"
## [8] "was" "the" "blurst" "of" "times."
Una cosa a tener en cuenta de pasada es que la salida aquí es una lista (se puede decir por la parte de la salida), cuyo primer y único elemento es un vector de caracteres. Esto es útil de muchas maneras, ya que significa que puedes ingresar un vector de caracteres para
x
y luego hacer que la función strsplit ()
los divida todos, pero es un poco molesto cuando solo tienes una sola entrada. Para ello, es útil saber que puedes deslistar ()
la salida:
unlist( monkey.1 )
## [1] "It" "was" "the" "best" "of" "times." "It"
## [8] "was" "the" "blurst" "of" "times."
Para entender por qué es importante recordar usar el argumento fixed = TRUE
, supongamos que queríamos dividir esto en dos oraciones separadas. Es decir, queremos usar split = “.”
como nuestra cadena delimitadora. Siempre y cuando le digamos a R que recuerde tratar esto como un carácter separador fijo, entonces obtenemos la respuesta correcta:
strsplit( x = monkey, split = ".", fixed = TRUE )
## [[1]]
## [1] "It was the best of times" " It was the blurst of times"
Sin embargo, si no hacemos esto, entonces R asumirá que cuando escribas split = “”.
estabas tratando de construir una “expresión regular”, y como sucede el personaje.
tiene un significado especial dentro de una expresión regular. Como consecuencia, si olvidas incluir la parte fija = VERDADERA
, no obtendrás las respuestas que estás buscando.
Hacer conversiones simples
Una tarea ligeramente diferente que surge con bastante frecuencia es hacer transformaciones al texto. Un ejemplo sencillo de esto sería convertir texto a minúsculas o mayúsculas, lo que puedes hacer usando las funciones toupper ()
y tolower ()
. Ambas funciones tienen un único argumento x
que contiene el texto que necesita ser convertido. Un ejemplo de esto se muestra a continuación:
text <- c( "lIfe", "Impact" )
tolower( x = text )
## [1] "life" "impact"
Una forma un poco más poderosa de hacer transformaciones de texto es usar la función chartr ()
, que permite especificar una sustitución “carácter por carácter”. Esta función contiene tres argumentos, viejo
, nuevo
y x
. Como de costumbre x
especifica el texto que necesita ser transformado. Los argumentos antiguos
y nuevos
son cadenas de la misma longitud, y especifican cómo se va a convertir x
. Cada instancia del primer personaje en la antigüedad
se convierte en el primer personaje en nuevo
y así sucesivamente. Por ejemplo, supongamos que quería convertir “albino”
a “libido”
. Para ello, necesito convertir todos los caracteres “a”
(todos 1) en “albino”
en caracteres “l”
(es decir, a
→ l
). Adicionalmente, necesito hacer las sustituciones l
→ i
y n
→ d
. Para ello, usaría el siguiente comando:
old.text <- "albino"
chartr( old = "aln", new = "lid", x = old.text )
## [1] "libido"
Aplicación de operaciones lógicas al texto
En la Sección 3.9.5 discutimos una herramienta de procesamiento de texto muy básica, a saber, la capacidad de usar el operador de igualdad ==
para probar para ver si dos cadenas son idénticas entre sí. Sin embargo, también puedes usar otros operadores lógicos. Por ejemplo, R también le permite usar los operadores <
y >
para determinar cuál de las dos cadenas viene primero, alfabéticamente hablando. Algo así como. En realidad, es un poco más complicado que eso, pero comencemos con un ejemplo sencillo:
"cat" < "dog"
## [1] TRUE
En este caso, vemos que “gato”
sí viene antes que “perro”
alfabéticamente, por lo que R juzga que la afirmación es cierta. No obstante, si le pedimos a R que nos diga si “gato”
viene antes que “oso hormiguero”
,
"cat" < "anteater"
## [1] FALSE
Nos dice que la declaración es falsa. Hasta el momento, tan bien. Pero los datos de texto son un poco más complicados de lo que sugiere el diccionario. ¿Qué pasa con “gato”
y “CAT”
? ¿Cuál de estos viene primero? Vamos a probarlo y averiguarlo:
"CAT" < "cat"
## [1] FALSE
En otras palabras, R asume que las letras mayúsculas vienen antes que las minúsculas. Bastante justo. Es probable que nadie se sorprenda por eso. Lo que podría resultarle sorprendente es que R asume que todas las letras mayúsculas vienen antes que todas las minúsculas. Es decir, mientras que “oso hormiguero” < “cebra”
es una declaración verdadera, y el equivalente en mayúscula “ANTEATER” < “ZEBRA”
también es cierto, no es cierto decir que “oso hormiguero” < “ZEBRA”
, como ilustra el siguiente extracto:
"anteater" < "ZEBRA"
## [1] TRUE
Esto puede parecer un poco contrario a la intuición. Con eso en mente, puede ser útil echar un vistazo rápido a la Tabla 7.3, que enumera varios caracteres de texto en el orden que usa R.
Tabla 7.3: El orden de los diversos caracteres de texto utilizados por los < and > operadores, así como por la función sort (). No se muestra el carácter “espacio”, que en realidad viene primero en la lista.
Personajes |
---|
! “# $% & '() * +, -./0 1 2 3 4 5 6 7 8 9:; < = >? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [] ^ _ 'a b c d e f g h i j k l m n o p q r s t u v w x y z} | { |
Una función de la que quiero hacer un punto de hablar, a pesar de que no es del todo sobre el tema, es la función cat ()
. La función cat ()
es una mezcla de paste ()
e print ()
. Es decir, lo que hace es concatenar cadenas y luego imprimirlas. En tu propio trabajo probablemente puedas sobrevivir sin él, ya que print ()
y paste ()
realmente harán lo que necesites, pero la función cat ()
es tan utilizada que creo que es una buena idea hablar de ello aquí. La idea básica detrás de cat ()
es sencilla. Al igual que paste ()
, toma varios argumentos como entradas, que convierte en cadenas, se contrae (usando un carácter separador especificado usando el argumento sep
) e imprime en pantalla. Si quieres, puedes usar el argumento file
para decirle a R que imprima la salida en un archivo en lugar de en pantalla (no voy a hacer eso aquí). Sin embargo, es importante tener en cuenta que la función cat ()
colapsa primero los vectores y luego los concatena. Es decir, observe que cuando uso cat ()
para combinar hw
y ng
, obtengo un resultado diferente que si hubiera usado paste ()
cat( hw, ng )
## hello world nasty government
paste( hw, ng, collapse = " " )
## [1] "hello nasty world government"
Observe la diferencia en el orden de las palabras. Hay algunos detalles adicionales que debo mencionar sobre cat ()
. En primer lugar, cat ()
realmente es una función para imprimir, y no para crear cadenas de texto para almacenar para más adelante. No se puede asignar la salida a una variable, como se ilustra en el siguiente ejemplo:
x <- cat( hw, ng )
## hello world nasty government
x
## NULL
A pesar de mi intento de almacenar la salida como una variable, cat ()
imprimió los resultados en pantalla de todos modos, y resulta que la variable que creé no contiene nada en absoluto. 122 En segundo lugar, la función cat ()
hace uso de una serie de caracteres “especiales”. Hablaré más sobre estos en la siguiente sección, pero voy a ilustrar el punto básico ahora, usando el ejemplo de “\ n”
que se interpreta como un carácter de “nueva línea”. Por ejemplo, compare el comportamiento de print ()
y cat ()
cuando se le pida que imprima la cadena “hello\ nworld”
:
print( "hello\nworld" ) # print literally:
## [1] "hello\nworld"
cat( "hello\nworld" ) # interpret as newline
## hello
## world
De hecho, este comportamiento es lo suficientemente importante como para que merezca una sección propia...
Usar caracteres de escape en el texto
El apartado anterior nos lleva de manera bastante natural a una cuestión bastante fundamental a la hora de tratar las cadenas, a saber, el tema de los delimitadores y los caracteres de escape. Reducida a su forma más básica, el problema que tenemos es que los comandos R se escriben usando caracteres de texto, y nuestras cadenas también consisten en caracteres de texto. Entonces, supongamos que quiero escribir la palabra “hola”, y que R la codifique como una cadena. Si solo tuviera que escribir hola
, R pensará que me refiero a una variable o una función llamada hola
en lugar de interpretarla como una cadena. La solución que adopta R es exigir que encierres tu cadena por caracteres delimitadores, que pueden ser comillas dobles o comillas simples. Entonces, cuando escribo “hola”
o 'hola'
entonces R sabe que debe tratar el texto entre las comillas como una cadena de caracteres. Sin embargo, esta no es una solución completa al problema: después de todo, "
y '
son en sí mismos caracteres de texto perfectamente legítimos, por lo que podríamos querer incluirlos en nuestra cadena también. Por ejemplo, supongamos que quería codificar el nombre “O'Rourke” como una cadena. No es legítimo para mí escribir 'O'rourke'
porque R es demasiado estúpido para darme cuenta de que “O'Rourke” es una palabra real. Entonces interpretará la parte 'O'
como una cadena completa, y luego se confundirá cuando llegue a la parte Rourke'
. Como consecuencia, lo que obtienes es un mensaje de error:
'O'Rourke'
Error: unexpected symbol in "'O'Rourke"
Hasta cierto punto, R nos ofrece una solución barata al problema por el hecho de que nos permite usar ya sea "
o '
como el carácter delimitador. Aunque 'O'rourke'
hará llorar a R, está perfectamente contento con “O'Rourke”
:
"O'Rourke"
## [1] "O'Rourke"
Esto es una ventaja real de tener dos caracteres delimitadores diferentes. Desafortunadamente, cualquiera que tenga hasta la más mínima desviación hacia ellos puede ver el problema con esto. Supongamos que estoy leyendo un libro que contiene el siguiente pasaje,
P.J. O'Rourke dice: “¡Sí, dinero!”. Es una broma, pero nadie se ríe.
y quiero ingresar esto como una cadena. Ni los delimitadores '
o "
resolverán el problema aquí, ya que esta cadena contiene tanto un carácter de comilla simple como un carácter de comilla doble. Para codificar cadenas como esta, tenemos que hacer algo un poco inteligente.
Cuadro 7.4: Caracteres de escape estándar que son evaluados por algunos comandos de procesamiento de texto, incluyendo cat ()
. Esta convención se remonta al desarrollo del lenguaje de programación C en la década de 1970, y como consecuencia muchos de estos personajes tienen más sentido si pretendes que R es en realidad una máquina de escribir, como se explica en el texto principal. ¿Tipo? Cotizaciones
para el archivo de ayuda R correspondiente.
Escape.sequence | Interpretación |
---|---|
\ n |
Newline |
\ t |
Pestaña Horizontal |
\ v |
Pestaña Vertical |
\ b |
Retrocesos |
\ r |
Retorno de carro |
\ f |
Formulario de alimentación |
\ a |
Sonido de alerta |
\\ |
Barra invertida |
\ ' |
Cotización simple |
\” |
Cotización doble |
La solución al problema es designar un carácter de escape, que en este caso es\
, la humilde barra inversa. El carácter de escape es un poco de cordero sacrificial: si incluyes un carácter de barra diagonal inversa en tu cadena, R no lo tratará como un carácter literal en absoluto. En realidad se usa como una forma de insertar caracteres “especiales” en tu cadena. Por ejemplo, si quieres forzar a R a insertar comillas reales en la cadena, entonces lo que realmente escribes es\ '
o\”
(estas se llaman secuencias de escape). Entonces, para codificar la cadena discutida anteriormente, aquí hay un comando que podría usar:
PJ <- "P.J. O\'Rourke says, \"Yay, money!\". It\'s a joke, but no-one laughs."
Observe que he incluido las barras invertida tanto para las comillas simples como para las comillas dobles. Eso en realidad es exagerado: ya que he usado "
como mi delimitador, solo necesitaba hacer esto para las comillas dobles. Sin embargo, el comando ha funcionado, ya que no recibí un mensaje de error. Ahora veamos qué pasa cuando lo imprimo:
print( PJ )
## [1] "P.J. O'Rourke says, \"Yay, money!\". It's a joke, but no-one laughs."
Hm. ¿Por qué R ha impreso la cadena usando\”
? Por exactamente la misma razón que necesitaba insertar la barra invertida en primer lugar. Es decir, cuando R imprime la cadena PJ
, la ha encerrado con caracteres delimitadores, y quiere mostrarnos sin ambigüedades cuáles de las comillas dobles son delimitadores y cuáles son realmente parte de la cadena. Afortunadamente, si esto te molesta, puedes hacer que desaparezca usando la función print.noquote ()
, que solo imprimirá la cadena literal que codificaste en primer lugar:
print.noquote( PJ )
Escribir cat (PJ)
producirá una salida similar.
Introducir el carácter de escape resuelve muchos problemas, ya que proporciona un mecanismo mediante el cual podemos insertar todo tipo de caracteres que no están en el teclado. Por ejemplo, en lo que respecta a una computadora, “nueva línea” es en realidad un carácter de texto. Es el personaje que se imprime cada vez que pulsas la tecla “retorno” en tu teclado. Si quieres insertar un nuevo carácter de línea en tu cadena, puedes hacerlo incluyendo la secuencia de escape\ n
. O bien, si quieres insertar un carácter de barras invertida, entonces puedes usar\\
. En la Tabla 7.4 se muestra una lista de las secuencias de escape estándar reconocidas por R. Muchos de estos en realidad se remontan a los días de la máquina de escribir (por ejemplo, el retorno del carro), por lo que pueden parecer un poco contradictorios para las personas que nunca han usado una. Para tener una idea de lo que hacen las diversas secuencias de escape, tendremos que usar la función cat ()
, porque es la única función “tonta” suficiente para imprimirlas literalmente:
cat( "xxxx\boo" ) # \b is a backspace, so it deletes the preceding x
cat( "xxxx\too" ) # \t is a tab, so it inserts a tab space
cat( "xxxx\noo" ) # \n is a newline character
cat( "xxxx\roo" ) # \r returns you to the beginning of the line
Y eso es prácticamente todo. Hay algunas otras secuencias de escape que R reconoce, que puede usar para insertar caracteres ASCII o Unicode arbitrarios en su cadena (escriba? Cotizaciones
para más detalles) pero no voy a entrar en detalles aquí.
Coincidencia y sustitución de texto
Otra tarea que muchas veces queremos resolver es encontrar todas las cadenas que coincidan con cierto criterio, y posiblemente incluso hacer alteraciones en el texto sobre esa base. Hay varias funciones en R que te permiten hacer esto, tres de las cuales voy a hablar brevemente aquí: grep ()
, gsub ()
y sub ()
. Al igual que la función substr ()
de la que hablé antes, estas tres funciones están pensadas para ser utilizadas en conjunto con expresiones regulares (ver Sección 7.8.9 pero también puedes usarlas de una manera más simple, ya que todas permiten establecer fixed = TRUE
, lo que significa que podemos ignora toda esta basura de expresión regular y solo usa una simple coincidencia de texto.
Entonces, ¿cómo funcionan estas funciones? Empecemos con la función grep ()
. El propósito de esta función es introducir un vector de cadenas de caracteres x
, y extraer todas aquellas cadenas que se ajusten a un determinado patrón. En nuestros ejemplos, asumiré que el patrón
en cuestión es una secuencia literal de caracteres que la cadena debe contener (eso es lo que fija = TRUE
hace). Para ilustrar esto, comencemos con un simple conjunto de datos, un vector que contiene los nombres de tres cervezas
. Algo como esto:
beers <- c( "little creatures", "sierra nevada", "coopers pale" )
A continuación, usemos grep ()
para averiguar cuál de estas cadenas contiene la subcadena “er”
. Es decir, el patrón
que necesitamos hacer coincidir es la cadena fija “er”
, por lo que el comando que necesitamos usar es:
grep( pattern = "er", x = beers, fixed = TRUE )
## [1] 2 3
Lo que aquí nos dice la salida es que el segundo y tercer elementos de las cervezas
contienen ambos la subcadena “er”
. Alternativamente, sin embargo, podríamos preferirlo si grep ()
devolvió las cadenas reales ellos mismos. Podemos hacer esto especificando value = TRUE
en nuestra llamada a la función. Es decir, usaríamos un comando como este:
grep( pattern = "er", x = beers, fixed = TRUE, value = TRUE )
## [1] "sierra nevada" "coopers pale"
Las otras dos funciones que quería mencionar en esta sección son gsub ()
y sub ()
. Ambos son similares en espíritu a grep ()
en la medida en que lo que hacen es buscar a través de las cadenas de entrada (x
) y encontrar todas las cadenas que coincidan con un patrón
. Sin embargo, lo que hacen estas dos funciones es reemplazar el patrón con una cadena de reemplazo
. La función gsub ()
reemplazará todas las instancias del patrón, mientras que la función sub ()
solo reemplaza la primera instancia de la misma en cada cadena. Para ilustrar cómo funciona esto, supongamos que quiero reemplazar todas las instancias de la letra “a”
por la cadena “BLAH”
. Puedo hacer esto a los datos de las cervezas
usando la función gsub ()
:
gsub( pattern = "a", replacement = "BLAH", x = beers, fixed = TRUE )
## [1] "little creBLAHtures" "sierrBLAH nevBLAHdBLAH"
## [3] "coopers pBLAHle"
Observe que las tres de las “a”
s en “sierra nevada”
han sido reemplazadas. Por el contrario, veamos qué sucede cuando usamos exactamente el mismo comando, pero esta vez usando la función sub ()
en su lugar:
sub( pattern = "a", replacement = "BLAH", x = beers, fixed = TRUE )
## [1] "little creBLAHtures" "sierrBLAH nevada" "coopers pBLAHle"
Sólo se cambia la primera “a”
.
Expresiones regulares (no realmente)
Hay una última cosa de la que quiero hablar con respecto a la manipulación de textos, y es el concepto de una expresión regular. A lo largo de esta sección a menudo hemos necesitado especificar fixed = TRUE
para obligar a R a tratar algunas de nuestras cadenas como cadenas reales, en lugar de como expresiones regulares. Entonces, antes de seguir adelante, quiero explicar muy brevemente qué son las expresiones regulares. No voy a hablar en absoluto sobre cómo funcionan o cómo los especificas, porque son realmente complicados y nada relevantes para este libro. Sin embargo, son herramientas extremadamente poderosas y son ampliamente utilizadas por personas que tienen que trabajar con muchos datos de texto (por ejemplo, personas que trabajan con datos de lenguaje natural), por lo que es útil al menos tener una vaga idea de lo que son. La idea básica es bastante simple. Supongamos que quiero extraer todas las cadenas en mi vector cervezas
que contengan una vocal seguida inmediatamente de la letra “s”
. Es decir, quiero encontrar los nombres de cerveza que contengan ya sea
, “as”
“es”
, “os”
o “nos”
. Una posibilidad sería especificar manualmente todas estas posibilidades y luego compararlas como cadenas fijas una a la vez, pero eso es tedioso. La alternativa es tratar de escribir una sola expresión “regular” que coincida con todas estas. La expresión regular que hace este 123 es “[aeiou] s”
, y se puede ver como que está haciendo la sintaxis aquí. La expresión entre corchetes significa “cualquiera de las cosas en el medio”, por lo que la expresión en su conjunto significa “cualquiera de las cosas en el medio” (es decir, vocales) seguida de la letra “s”
. Cuando se aplica a nuestros nombres de cerveza obtenemos esto:
grep( pattern = "[aeiou]s", x = beers, value = TRUE )
## [1] "little creatures"
Entonces resulta que sólo “pequeñas criaturas”
contiene una vocal seguida de la letra “s”
. Pero claro, si los datos hubieran contenido una cerveza como “fosters”
, que habría coincidido también porque contiene la cadena “os”
. No obstante, elegí deliberadamente no incluirlo porque Fosters no es —en mi opinión— una cerveza propiamente dicha. 124 Como se puede decir de este ejemplo, las expresiones regulares son una herramienta ordenada para especificar patrones en el texto: en este caso, “vocal entonces s”. Entonces definitivamente son cosas que vale la pena conocer si alguna vez te encuentras necesitando trabajar con un gran cuerpo de texto. No obstante, al ser bastante complejas y no necesarias para ninguna de las aplicaciones que se discuten en este libro, no voy a hablar más de ellas.