7.2: Transformar y recodificar una variable
- Page ID
- 151614
\( \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}\)No es raro en el análisis de datos del mundo real encontrar que una de tus variables no es del todo equivalente a la variable que realmente quieres. Por ejemplo, a menudo es conveniente tomar una variable de valor continuo (por ejemplo, edad) y dividirla en un número pequeño de categorías (por ejemplo, más joven, medio, mayor). En otras ocasiones, es posible que necesite convertir una variable numérica en una variable numérica diferente (por ejemplo, es posible que desee analizar en el valor absoluto de la variable original). En esta sección voy a describir algunos trucos clave que puedes hacer uso para hacer esto.
Creación de una variable transformada
El primer truco a discutir es la idea de transformar una variable. Tomado literalmente, cualquier cosa que le hagas a una variable es una transformación, pero en la práctica lo que suele significar es que aplicas una función matemática relativamente simple a la variable original, con el fin de crear una nueva variable que o bien (a) proporciona una mejor manera de describir la cosa en realidad te interesa o (b) está más de acuerdo con los supuestos de las pruebas estadísticas que quieres hacer. Ya que —en esta etapa— no he hablado de pruebas estadísticas ni de sus suposiciones, te voy a mostrar un ejemplo basado en el primer caso.
Para que la explicación sea simple, la variable que intentaremos transformar (likert.raw
) no está dentro de un marco de datos, aunque en la vida real casi con certeza lo estaría. No obstante, creo que es útil comenzar con un ejemplo que no utilice marcos de datos porque ilustra el hecho de que ya se sabe hacer transformaciones variables. Para ver esto, pasemos por un ejemplo. Supongamos que he realizado un breve estudio en el que le hago una sola pregunta a 10 personas:
En una escala de 1 (fuertemente en desacuerdo) a 7 (muy de acuerdo), ¿en qué medida estás de acuerdo con la proposición de que “Los dinosaurios son increíbles”?
Ahora carguemos y veamos los datos. El archivo de datos Likert.rdata
contiene una única variable que contiene las respuestas de escala Likert sin procesar:
load("./rbook-master/data/likert.Rdata")
likert.raw
## [1] 1 7 3 4 4 4 2 6 5 5
No obstante, si lo piensas, esta no es la mejor manera de representar estas respuestas. Debido a la manera bastante simétrica en la que configuramos la escala de respuesta, existe un sentido en el que el punto medio de la escala debería haber sido codificado como 0 (sin opinión), y los dos puntos finales deberían ser +3 (fuerte acuerdo) y −3 (fuerte desacuerdo). Al recodificar los datos de esta manera, es un poco más reflexivo de cómo pensamos realmente sobre las respuestas. La recodificación aquí es trivialmente fácil: simplemente restamos 4 de los puntajes brutos:
likert.centred <- likert.raw - 4
likert.centred
## [1] -3 3 -1 0 0 0 -2 2 1 1
Una razón por la que podría ser útil tener los datos en este formato es que hay muchas situaciones en las que quizás prefieras analizar la fuerza de la opinión por separado de la dirección de la opinión. Podemos hacer dos transformaciones diferentes sobre esta variable centrada en likert.para
distinguir entre estos dos conceptos diferentes. En primer lugar, para calcular una variable opinion.strength
, queremos tomar el valor absoluto de los datos centrados (usando la función abs ()
que hemos visto anteriormente), así:
opinion.strength <- abs( likert.centred )
opinion.strength
## [1] 3 3 1 0 0 0 2 2 1 1
En segundo lugar, para calcular una variable que contiene sólo la dirección de la opinión e ignora la fuerza, podemos usar la función sign ()
para hacer esto. Si escribes?
sign verás que esta función es realmente simple: todos los números negativos se convierten a −1, todos los números positivos se convierten a 1 y cero se queda como 0. Entonces, cuando aplicamos la función sign ()
obtenemos lo siguiente:
opinion.dir <- sign( likert.centred )
opinion.dir
## [1] -1 1 -1 0 0 0 -1 1 1 1
Y ya terminamos. Ahora tenemos tres nuevas variables brillantes, todas las cuales son transformaciones útiles de los datos originales de likert.raw
. Todo esto debería parecerte bastante familiar. ¡Las herramientas que usas para hacer cálculos regulares en R (por ejemplo, Capítulos 3 y 4) son muy las mismas que usas para transformar tus variables! Para ello, en la Sección 7.3 revisaré el tema de hacer cálculos en R porque hay muchas otras funciones y operaciones que vale la pena conocer.
Antes de seguir adelante, puede que sienta curiosidad por ver cómo se ven estos cálculos si los datos hubieran comenzado en un marco de datos. Para ello, puede ser útil señalar que el siguiente ejemplo realiza todos los cálculos utilizando variables dentro de un marco de datos, y almacena las variables creadas en su interior:
df <- data.frame( likert.raw ) # create data frame
df$likert.centred <- df$likert.raw - 4 # create centred data
df$opinion.strength <- abs( df$likert.centred ) # create strength variable
df$opinion.dir <- sign( df$likert.centred ) # create direction variable
df
## likert.raw likert.centred opinion.strength opinion.dir
## 1 1 -3 3 -1
## 2 7 3 3 1
## 3 3 -1 1 -1
## 4 4 0 0 0
## 5 4 0 0 0
## 6 4 0 0 0
## 7 2 -2 2 -1
## 8 6 2 2 1
## 9 5 1 1 1
## 10 5 1 1 1
En otras palabras, los comandos que usas son básicamente unos como antes: es solo que cada vez que quieres leer una variable del marco de datos o escribir en el marco de datos, usas el operador $
. Esa es la forma más fácil de hacerlo, aunque debería tomar nota del hecho de que la gente a veces hace uso de la función within ()
para hacer lo mismo. Sin embargo, dado que (a) no uso la función within ()
en ningún otro lugar de este libro, y (b) el operador $
funciona bien, no voy a discutirlo más lejos.
Cortar una variable numérica en categorías
Una tarea pragmática que surge con más frecuencia de lo que piensas es el problema de cortar una variable numérica en categorías discretas. Por ejemplo, supongamos que me interesa ver la distribución por edades de las personas en una reunión social:
age <- c( 60,58,24,26,34,42,31,30,33,2,9 )
En algunas situaciones puede ser bastante útil agruparlos en un número pequeño de categorías. Por ejemplo, podríamos agrupar los datos en tres categorías amplias: jóvenes (0-20), adultos (21-40) y mayores (41-60). Esta es una clasificación bastante gruesa, y las etiquetas que he adjuntado solo tienen sentido en el contexto de este conjunto de datos (por ejemplo, visto de manera más general, un niño de 42 años no se consideraría a sí mismo como “mayor”). Podemos cortar esta variable con bastante facilidad usando la función cut ()
. 105 Para hacer las cosas un poco más limpias, comenzaré por crear una variable que defina los límites para las categorías:
age.breaks <- seq( from = 0, to = 60, by = 20 )
age.breaks
## [1] 0 20 40 60
y otra para las etiquetas:
age.labels <- c( "young", "adult", "older" )
age.labels
## [1] "young" "adult" "older"
Tenga en cuenta que hay cuatro números en la variable age.breaks
, pero solo tres etiquetas en la variable age.labels
; lo he hecho porque la función cut ()
requiere que especifique los bordes de las categorías en lugar de los puntos medios. En cualquier caso, ahora que hemos hecho esto, podemos usar la función cut ()
para asignar cada observación a una de estas tres categorías. Hay varios argumentos para la función cut ()
, pero los tres que debemos preocuparnos son:
x
. La variable que necesita ser categorizada.descansos
. Se trata de un vector que contiene las ubicaciones de los descansos que separan las categorías, o bien un número que indica cuántas categorías desea.etiquetas
. Las etiquetas adjuntas a las categorías. Esto es opcional: si no especifica esta R adjuntará una etiqueta aburrida que muestre el rango asociado a cada categoría.
Como ya hemos creado variables correspondientes a los descansos y las etiquetas, el comando que necesitamos es simplemente:
age.group <- cut( x = age, # the variable to be categorised
breaks = age.breaks, # the edges of the categories
labels = age.labels ) # the labels for the categories
Tenga en cuenta que la variable de salida aquí es un factor. Para ver lo que realmente ha hecho este comando, podríamos imprimir la variable age.group
, pero creo que en realidad es más útil crear un marco de datos que incluya tanto la variable original como la categorizada, para que puedas ver las dos lado a lado:
data.frame(age, age.group)
## age age.group
## 1 60 older
## 2 58 older
## 3 24 adult
## 4 26 adult
## 5 34 adult
## 6 42 older
## 7 31 adult
## 8 30 adult
## 9 33 adult
## 10 2 young
## 11 9 young
También puede ser útil tabular la salida, solo para ver si tienes una buena división uniforme de la muestra:
table( age.group )
## age.group
## young adult older
## 2 6 3
En el ejemplo anterior, yo mismo tomé todas las decisiones. Al igual que la función hist ()
que vimos en el Capítulo 6, si quieres puedes delegar muchas de las opciones a R. Por ejemplo, si quieres puedes especificar el número de categorías que deseas, en lugar de darles rangos explícitos, y puedes permitir que R se le ocurra algunas etiquetas para las categorías. Para darte una idea de cómo funciona esto, echa un vistazo al siguiente ejemplo:
age.group2 <- cut( x = age, breaks = 3 )
Con este comando, he pedido tres categorías, pero que R tome las decisiones de dónde deberían estar los límites. No me molestaré en imprimir la variable age.group2
, porque no es terriblemente bonita ni muy interesante. En cambio, toda la información importante se puede extraer mirando los datos tabulados:
table( age.group2 )
## age.group2
## (1.94,21.3] (21.3,40.7] (40.7,60.1]
## 2 6 3
Esta salida requiere un poco de interpretación, pero no es complicada. Lo que ha hecho R se determina que la categoría de edad más baja debe ir de 1.94 años a 21.3 años, la segunda categoría debe ir de 21.3 años a 40.7 años, y así sucesivamente. El formato en esas etiquetas puede parecer un poco divertido para aquellos de ustedes que no han estudiado muchas matemáticas, pero es bastante simple. Cuando R describe la primera categoría como correspondiente al rango (1.94,21.3] lo que dice es que el rango consiste en aquellos números que son mayores que 1.94 pero menores o iguales a 21.3. En otras palabras, los raros paréntesis asimétricos es la forma de R s de decirte que si resulta que hay un valor que es exactamente igual a 21.3, entonces pertenece a la primera categoría, no a la segunda. Obviamente, esto en realidad no es posible ya que solo he especificado las edades al número entero más cercano, pero R no lo sabe y así está tratando de ser preciso por si acaso. Esta notación es en realidad bastante estándar, pero sospecho que no todos los que leen el libro lo habrán visto antes. En cualquier caso, esas etiquetas son bastante feas, por lo que suele ser una buena idea especificar tus propias etiquetas significativas para las categorías.
Antes de seguir adelante, debería tomarme un momento para hablar un poco sobre la mecánica de la función cut ()
. Observe que R ha tratado de dividir la variable de edad
en tres contenedores de tamaño aproximadamente igual. A menos que especifiques los descansos particulares que deseas, eso es lo que hará. Pero supongamos que se quiere dividir la variable de edad
en tres categorías de diferente tamaño, pero con números aproximadamente idénticos de personas. ¿Cómo harías eso? Bueno, si ese es el caso, entonces lo que quieres hacer es que los descansos correspondan a los percentiles 0, 33, 66 y 100 de los datos. Una forma de hacer esto sería calcular esos valores usando la función quantiles ()
y luego usar esos cuantiles como entrada a la función cut ()
. Eso es bastante fácil de hacer, pero se necesitan un par de líneas para escribir. Entonces, en cambio, el paquete lsr
tiene una función llamada quantileCut ()
que hace exactamente esto:
age.group3 <- quantileCut( x = age, n = 3 )
table( age.group3 )
## age.group3
## (1.94,27.3] (27.3,33.7] (33.7,60.1]
## 4 3 4
Observe la diferencia en los límites que selecciona la función quantileCut ()
. La primera y tercera categoría ahora abarcan un rango de edad de aproximadamente 25 años cada una, mientras que la categoría media se ha reducido a un lapso de sólo 6 años. Hay algunas situaciones en las que esto es genuinamente lo que quieres (¡por eso escribí la función!) , pero en general debes tener cuidado. Por lo general, la variable numérica que intentas cortar en categorías ya se expresa en unidades significativas (es decir, es una escala de intervalos), pero si la cortas en tamaños de bin desiguales, a menudo es muy difícil adjuntar interpretaciones significativas a las categorías resultantes.
De manera más general, independientemente de si estás usando la función original cut ()
o la versión quantileCut ()
, es importante tomarse el tiempo para averiguar si las categorías resultantes tienen algún sentido en términos de tu proyecto de investigación. Si no tienen ningún sentido para ti como categorías significativas, entonces cualquier análisis de datos que use esas categorías probablemente no tenga sentido. De manera más general, en la práctica he notado que las personas tienen un deseo muy fuerte de tallar sus datos (continuos y desordenados) en algunas categorías (discretas y simples); y luego ejecutar análisis usando los datos categorizados en lugar de los originales. 106 No iría tan lejos como para decir que esta es una idea inherentemente mala, pero sí tiene algunos inconvenientes bastante serios a veces, así que le aconsejaría algo de precaución si está pensando en hacerlo.