Sesión 3

Conceptos clave previos

Antes de adentrarnos en la ejecución matemática y visual del análisis de correspondencias, es fundamental que repasemos cuatro conceptos estadísticos sobre los que se cimenta esta técnica:

  • Frecuencia observada y esperada: La frecuencia observada es el recuento real de casos empíricos que obtenemos en nuestra tabla de contingencia (las respuestas directas de nuestra encuesta). Por otro lado, la frecuencia esperada es el recuento teórico que deberíamos encontrar en cada celda si no existiera absolutamente ninguna relación (independencia estadística) entre las variables de fila y columna.
  • Residuo: Es la diferencia matemática entre lo que observamos en la realidad y lo que esperábamos observar de forma teórica. Un residuo positivo señala una “atracción” o asociación mayor de lo normal entre dos categorías, mientras que un residuo negativo indica una falta de vinculación o “repulsión”.
  • Prueba \(\chi^2\) (Chi-cuadrado): Es el contraste estadístico que evalúa el conjunto total de los residuos de nuestra tabla. Nos permite confirmar si las discrepancias entre lo observado y lo esperado son lo suficientemente grandes como para descartar que sean fruto del azar, validando así que existe una asociación significativa en el mercado.
  • Mapa cartesiano: Es el producto estrella del análisis de correspondencias. Consiste en la proyección geométrica bidimensional (el plano formado por los ejes coordenados) de las relaciones ocultas en la tabla de contingencia. En este mapa, la inercia y los residuos se traducen en distancias y ángulos, permitiéndonos leer visualmente el posicionamiento competitivo de forma intuitiva.

El concepto de distancia y mapa cartesiano

Algunas consideraciones sobre la representación gráfica de dos variables en un plano cartesiano

  • X -> latitud
  • Y -> longitud
  • X no tiene porqué estar medida en igual escala que Y, de modo que sólo interpretamos distancias
df <- readRDS('data/distancias.rds')
kable(df)
Ciudad Albacete Alicante Almería Avila Badajoz Barcelona Bilbao Burgos Cáceres Cádiz Castellón Ciudad Real Córdoba A Coruña Cuenca Gerona Granada Guadalajara Huelva Huesca Jaén León Lérida Logroño Lugo Madrid Màlaga Murcia Orense Oviedo Palencia Pamplona Pontevedra Salamanca S. Sebastián / Donostia Santander Segovia Sevilla Soria Tarragona Teruel Toledo Valencia Valladolid Vitoria / Gazteiz Zamora Zaragoza
Albacete 0 171 369 366 525 540 646 488 504 617 256 207 354 860 142 640 363 309 506 495 264 584 515 578 762 251 473 150 772 702 491 598 874 463 691 644 338 492 473 443 242 240 191 444 602 499 423
Alicante 171 0 294 537 696 515 817 659 675 688 231 378 525 1031 313 615 353 480 703 570 415 855 490 653 933 422 482 75 943 873 662 673 1045 634 766 815 509 609 548 417 317 411 166 615 739 670 498
Almería 369 294 0 663 604 809 958 800 651 484 525 407 332 1172 511 909 166 621 516 830 228 896 802 899 1074 563 219 219 1084 1014 803 970 1186 763 1032 956 650 422 794 711 577 526 460 756 914 811 758
Avila 366 537 663 0 318 717 401 243 229 618 532 256 457 538 282 817 534 173 552 490 435 255 558 358 440 115 644 516 443 373 168 437 545 97 475 369 67 493 261 649 417 137 467 121 357 159 418
Badajoz 525 696 604 318 0 1022 694 536 89 342 805 318 272 772 555 1122 438 459 251 798 376 496 866 676 674 401 436 675 645 614 461 755 747 299 768 662 385 217 579 935 703 368 716 414 650 361 726
Barcelona 540 515 809 717 1022 0 620 583 918 1284 284 811 908 1118 562 100 868 563 1140 274 804 784 156 468 1020 621 997 590 1027 902 669 437 1129 778 529 693 650 1046 453 98 409 692 349 663 530 759 296
Bilbao 646 817 958 401 694 620 0 158 605 1058 607 585 795 644 562 720 829 396 939 322 730 359 464 152 546 395 939 796 605 304 244 159 707 395 119 108 355 993 257 555 488 466 633 280 66 376 324
Burgos 488 659 800 243 536 583 158 0 447 900 524 427 637 535 404 683 671 238 781 359 572 201 427 115 437 237 781 638 447 322 86 203 549 237 232 156 197 775 141 518 372 308 517 122 114 218 287
Cáceres 504 675 651 229 89 918 605 447 0 369 701 324 319 683 451 1018 485 355 323 694 423 407 762 595 585 297 506 654 556 525 372 650 658 210 679 573 296 264 490 831 599 264 636 325 561 272 622
Cádiz 617 688 484 618 342 1284 1058 900 369 0 873 464 263 1072 708 1384 335 721 219 1060 367 796 1128 999 974 663 265 613 945 914 761 1070 1047 599 1132 1056 750 125 894 1059 873 583 808 714 1014 661 988
Castellón 256 231 525 532 805 284 607 524 701 873 0 463 610 1026 305 384 584 396 856 355 520 725 259 455 928 417 713 306 938 868 596 458 1040 629 551 680 504 762 383 186 152 437 65 563 574 665 283
Ciudad Real 207 378 407 256 318 811 585 427 324 464 463 0 201 799 244 911 278 248 433 587 179 511 655 526 696 190 388 357 699 641 424 597 701 353 659 583 277 339 421 649 397 119 398 377 541 415 515
Córdoba 354 525 332 457 272 908 795 637 319 263 610 201 0 995 445 1008 166 458 232 797 104 733 865 736 897 400 187 444 921 851 640 807 977 529 869 793 497 138 631 796 596 320 545 578 751 616 725
A Coruña 860 1031 1172 538 772 1118 644 535 683 1072 1026 799 995 0 776 1218 1043 667 1006 905 944 334 973 650 98 609 1153 1010 175 340 450 738 121 473 763 547 560 947 676 1064 907 675 961 455 549 411 833
Cuenca 142 313 511 282 555 562 562 404 451 708 305 244 445 776 0 662 479 486 677 406 380 500 472 464 678 167 615 292 689 618 407 535 791 379 636 560 254 583 359 464 153 187 220 360 518 415 334
Gerona 640 615 909 817 1122 100 720 683 1018 1384 384 911 1008 1218 662 0 968 663 1240 374 904 884 256 568 1120 721 1097 690 1127 1002 769 537 1229 878 629 793 750 1146 553 198 509 792 449 763 630 859 396
Granada 363 353 166 534 438 868 829 671 485 335 584 278 166 1043 479 968 0 492 350 831 99 761 861 770 945 434 129 278 955 885 674 841 1058 631 903 827 521 256 665 770 605 397 519 627 785 682 759
Guadalajara 309 480 621 173 459 563 396 238 355 721 396 248 458 667 486 663 492 0 690 339 393 391 407 278 569 58 602 459 579 509 298 349 681 270 441 394 145 596 173 476 244 129 410 251 352 306 267
Huelva 506 703 516 552 251 1140 939 781 323 219 856 433 232 1006 677 1240 350 690 0 1029 336 730 1097 968 908 632 313 628 879 821 695 1039 981 533 1101 962 619 94 863 1029 842 552 791 648 919 595 957
Huesca 495 570 830 490 798 274 322 359 694 1060 355 587 797 905 406 374 831 339 1029 0 732 560 118 244 807 397 941 611 803 626 442 163 905 554 255 430 426 935 229 209 253 468 398 439 256 535 72
Jaén 264 415 228 435 376 804 730 572 423 367 520 179 104 944 380 904 99 393 336 732 0 668 779 671 846 335 209 340 856 786 575 742 957 532 804 728 422 242 566 707 506 298 455 528 686 583 660
León 584 855 896 255 496 784 359 201 407 796 725 511 733 334 500 884 761 391 730 560 668 0 628 316 236 333 877 734 271 118 130 404 373 197 433 293 245 671 342 719 573 392 685 134 315 135 488
Lérida 515 490 802 558 866 156 464 427 762 1128 259 655 865 973 472 256 861 407 1097 118 779 628 0 312 875 465 1009 583 871 703 513 281 973 622 373 537 494 1003 297 91 319 536 324 507 374 603 140
Logroño 578 653 899 358 676 468 152 115 595 999 455 526 736 650 464 568 770 278 968 244 671 316 312 0 352 336 880 694 562 391 201 88 664 352 169 225 299 874 105 403 336 407 481 237 86 333 172
Lugo 762 933 1074 440 674 1020 546 437 585 974 928 696 897 98 678 1120 945 569 908 807 846 236 875 352 0 511 1055 912 95 242 352 640 148 375 665 449 462 849 578 966 809 577 863 357 451 313 735
Madrid 251 422 563 115 401 621 395 237 297 663 417 190 400 609 167 721 434 58 632 397 335 333 465 336 511 0 544 401 521 451 240 407 623 212 469 393 87 538 231 534 302 71 352 193 351 248 325
Málaga 473 482 219 644 436 997 939 781 506 265 713 388 187 1153 615 1097 129 602 313 941 209 877 1009 880 1055 544 0 407 1065 995 784 951 1153 756 1013 937 631 219 775 899 715 507 648 737 895 792 869
Murcia 150 75 219 516 675 590 796 638 654 613 306 357 444 1010 292 690 278 459 628 611 340 734 583 694 912 401 407 0 922 852 641 714 1024 613 807 794 488 534 589 492 385 390 241 594 752 649 539
Orense 772 943 1084 443 645 1027 605 447 556 945 938 699 921 175 689 1127 955 579 879 803 856 271 871 562 95 521 1065 922 0 337 361 650 102 346 679 544 467 820 574 962 805 580 873 356 561 284 731
Oviedo 702 873 1014 373 614 902 304 322 525 914 868 641 851 340 618 1002 885 509 821 626 786 118 703 391 242 451 995 852 337 0 248 463 390 315 423 207 363 789 463 835 694 510 803 252 340 253 604
Palencia 491 662 803 168 461 669 244 86 372 761 596 424 640 450 407 769 674 298 695 442 575 130 513 201 352 240 784 641 361 248 0 289 463 162 318 201 158 636 213 604 444 305 592 47 200 143 373
Pamplona 598 673 970 437 755 437 159 203 650 1070 458 597 807 738 535 537 841 349 1039 163 742 404 281 88 640 407 951 714 650 463 289 0 752 440 92 267 370 945 176 372 356 478 501 325 93 421 175
Pontevedra 874 1045 1186 545 747 1129 707 549 658 1047 1040 701 977 121 791 1229 1058 681 981 905 957 373 973 664 148 623 1153 1024 102 390 463 752 0 448 781 666 569 922 676 1064 907 682 975 458 663 386 833
Salamanca 463 634 763 97 299 778 395 237 210 599 629 353 529 473 379 878 631 270 533 554 532 197 622 352 375 212 756 613 346 315 162 440 448 0 469 363 164 474 325 713 514 234 564 115 351 62 482
S.Sebastián 691 766 1032 475 768 529 119 232 679 1132 551 659 869 763 636 629 903 441 1101 255 804 433 373 169 665 469 1013 807 679 423 318 92 781 469 0 227 429 1007 268 464 449 540 594 354 118 450 268
Santander 644 815 956 369 662 693 108 156 573 1056 680 583 793 547 560 793 827 394 962 430 728 293 537 225 449 393 937 794 544 207 201 267 666 363 227 0 359 837 297 628 528 464 673 248 174 344 397
Segovia 338 509 650 67 385 650 355 197 296 750 504 277 497 560 254 750 521 145 619 426 422 245 494 299 462 87 631 488 467 363 158 370 569 164 429 359 0 560 194 585 389 158 439 111 311 182 354
Sevilla 492 609 422 493 217 1046 993 775 264 125 762 339 138 947 583 1146 256 596 94 935 242 671 1003 874 849 538 219 534 820 789 636 945 922 474 1007 837 560 0 769 949 748 458 697 589 825 536 863
Soria 473 548 794 261 579 453 257 141 490 894 383 421 631 676 359 553 665 173 863 229 566 342 297 105 578 231 775 589 574 463 213 176 676 325 268 297 194 769 0 388 231 302 376 210 191 306 157
Tarragona 443 417 711 649 935 98 555 518 831 1059 186 649 796 1064 464 198 770 476 1029 209 707 719 91 403 966 534 899 492 962 835 604 372 1064 713 464 628 585 949 388 0 311 605 251 598 489 694 231
Teruel 242 317 577 417 703 409 488 372 599 873 152 397 596 907 153 509 605 244 842 253 506 573 319 336 809 302 715 385 805 694 444 356 907 514 449 528 389 748 231 311 0 340 145 441 422 537 181
Toledo 240 411 526 137 368 692 466 308 264 583 437 119 320 675 187 792 397 129 552 468 298 392 536 407 577 71 507 390 580 510 305 478 682 234 540 464 158 458 302 605 340 0 372 258 422 296 396
Valencia 191 166 460 467 716 349 633 517 636 808 65 398 545 961 220 449 519 410 791 398 455 685 324 481 863 352 648 241 873 803 592 501 975 564 594 673 439 697 376 251 145 372 0 545 576 600 326
Valladolid 444 615 756 121 414 663 280 122 325 714 563 377 578 455 360 763 627 251 648 439 528 134 507 237 357 193 737 594 356 252 47 325 458 115 354 248 111 589 210 598 441 258 545 0 236 96 367
Vitoria 602 739 914 357 650 530 66 114 561 1014 574 541 751 549 518 630 785 352 919 256 686 315 374 86 451 351 895 752 561 340 200 93 663 351 118 174 311 825 191 489 422 422 576 236 0 332 258
Zamora 499 670 811 159 361 759 376 218 272 661 665 415 616 411 415 859 682 306 595 535 583 135 603 333 313 248 792 649 284 253 143 421 386 62 450 344 182 536 306 694 537 296 600 96 332 0 463
Zaragoza 423 498 758 418 726 296 324 287 622 988 283 515 725 833 334 396 759 267 957 72 660 488 140 172 735 325 869 539 731 604 373 175 833 482 268 397 354 863 157 231 181 396 326 367 258 463 0
datos <- as.matrix(df[, -1])
rownames(datos) <- colnames(datos)

fit <- cmdscale(datos, eig = TRUE, k = 2)

# Gráfico de la solución
x <- fit$points[, 2]
y <- fit$points[, 1]


# Generate the plot using modern ggplot2 syntax
ggplot(data = datos, aes(x = x,y = y,label = colnames(datos))) +
  geom_text(color = "black",vjust = 0,nudge_y = 0.5) +
  labs(x = "Coordenada 1", y = "Coordenada 2") +
  theme_minimal()

Introducción a la técnica del análisis de correspondencias simple

El análisis de correspondencias (CA) es una técnica de interdependencia cuyo objetivo es la representación geométrica de las relaciones bidimensionales derivadas de una tabla de contingencia.

Trabaja con variables categóricas o no métricas (frecuencias, recuentos), y nos permite pasar “del caos de los datos a la claridad de un mapa”. Su principal ventaja es que nos ayuda a visualizar la asociación o similitud entre los niveles de las variables de fila y columna basándose en el concepto de distancias (distancia \(\chi^2\)).

Planteamiento del problema a resolver (Caso HATCO)

Imagina que eres el Director de Marketing de HATCO. Has identificado a 9 competidores principales (empresas A hasta I) y has pedido a tus clientes que asocien a estas empresas (incluyendo la tuya) con 8 atributos competitivos:

  • x1: Rapidez del servicio
  • x2: Nivel de precios
  • x3: Flexibilidad de precios
  • x4: Imagen del fabricante
  • x5: Calidad del servicio
  • x6: Imagen de los vendedores
  • x7: Calidad del producto
  • x8: Tamaño de la empresa

Nuestras preguntas estratégicas son:

  1. ¿Quiénes son nuestros verdaderos competidores?
  2. ¿En qué nos diferenciamos?
  3. ¿Hay algún hueco en el mercado que no estemos cubriendo?

Sin embargo hay que ser cuidadoso respecto al sentido del análisis en función de la representación de los datos. En nuestro ejemplo debes verificar teóricamente que …

  • Los poseedores de los atributos son comparables respecto a esos atributos.
  • Confirmar que las empresas A,B, …, I son efectivamente competidoras de HATCO.
  • Que basan su diferenciación en los atributos X1 a X8.
  • El listado de atributos debe ser exhaustivo y no dejamos ninguno relevante para la caracterización de las empresas.

Preparación del entorno y los datos

Vamos a cargar las librerías necesarias y a leer nuestra matriz de datos. En este caso, usaremos el paquete FactoMineR para los cálculos matemáticos y factoextra para extraer visualizaciones modernas basadas en ggplot2.

# Load the contingency table (assuming the CSV has companies as columns and attributes as rows)
# For reproducibility, we structure the matrix directly if the CSV is not present
hatco_matrix <- matrix(
  c(
    16, 13, 8, 13, 9, 17, 15, 16, 6, 12,
    14, 14, 10, 11, 11, 14, 12, 13, 10, 14,
    6,  6,  14, 10, 11, 8,  7,  4,  14, 4,
    15, 18, 9,  2,  3,  15, 16, 7,  8,  8,
    15, 14, 6,  4,  4,  15, 14, 13, 7,  13,
    7,  18, 13, 4,  9,  16, 14, 5,  4,  16,
    4,  3,  1,  13, 9,  6,  3,  18, 2,  10,
    15, 16, 15, 11, 11, 14, 16, 12, 14, 14
    ),
  nrow = 8, byrow = TRUE,
  dimnames = list(
    c("x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"),
    c("hatco", "A", "B", "C", "D", "E", "F", "G", "H", "I")
  )
)

# Convert to table object
hatco_table <- as.table(hatco_matrix)
kable(hatco_table)
hatco A B C D E F G H I
x1 16 13 8 13 9 17 15 16 6 12
x2 14 14 10 11 11 14 12 13 10 14
x3 6 6 14 10 11 8 7 4 14 4
x4 15 18 9 2 3 15 16 7 8 8
x5 15 14 6 4 4 15 14 13 7 13
x6 7 18 13 4 9 16 14 5 4 16
x7 4 3 1 13 9 6 3 18 2 10
x8 15 16 15 11 11 14 16 12 14 14

Visualización exploratoria

💡 Antes del análisis matemático, un balloonplot nos ayuda a ver gráficamente la tabla de frecuencias absolutas: a mayor diámetro del círculo, mayor es la asociación mencionada por los clientes. Estos aspectos son típicas tareas de un buen analista que busca la mejor representación (la más visual) para su cliente.

# Visualizing the contingency table 
balloonplot(
  t(hatco_table), 
  main = "Brand vs Attribute Frequency", 
  xlab = "Company", 
  ylab = "Attribute",
  label = TRUE, 
  show.margins = TRUE,
  dotcolor = "lightsalmon")

Prueba de homogeneidad (Chi-cuadrado)

Antes de mapear, debemos confirmar empíricamente que existe dependencia estadística entre las filas (atributos) y las columnas (empresas). La hipótesis nula (\(H_0\)) es la independencia: las empresas no se diferencian por estos atributos.

# Perform Chi-Square test on the contingency table
output.chisq_test <- chisq.test(hatco_table)
print(output.chisq_test )

    Pearson's Chi-squared test

data:  hatco_table
X-squared = 122.6, df = 63, p-value = 1.033e-05

Al obtener un p-value < 0.05, rechazamos \(H_0\). Existe una asociación significativa entre las marcas y los atributos. Podemos proceder a mapear.

¿Si no podemos rechazar la \(H_0\) no se debe seguir adelante? No hay una respuesta verdadera. Es claro que no habrán relaciones de mucho poder, pero las relaciones se reprfesentan en el plano de igual forma.

🤔 ¿Qué marcas o qué atributos determinana las diferencias? Esta es la primera pregunta a resolver y para ello nuestra respuesta comienza en el análisis de los residuos.

Residuos de la tabla

Una vez confirmado que existe una relación significativa, el primer paso es explorar los residuos. Estos representan la diferencia entre las frecuencias observadas en nuestra muestra y las frecuencias que esperaríamos encontrar si las variables fueran totalmente independientes (frecuencias esperadas).

# Accessing raw residuals from the chi-square object
kable(round(output.chisq_test$residuals, 3))
hatco A B C D E F G H I
x1 0.676 -0.512 -0.947 0.953 -0.268 0.402 0.199 0.855 -1.148 -0.374
x2 0.193 -0.193 -0.297 0.374 0.423 -0.302 -0.539 0.079 0.197 0.234
x3 -1.022 -1.282 2.373 1.269 1.706 -0.734 -0.832 -1.590 2.994 -1.662
x4 1.235 1.694 -0.007 -2.137 -1.756 0.719 1.323 -1.066 0.103 -0.852
x5 1.083 0.399 -1.103 -1.516 -1.484 0.568 0.587 0.650 -0.360 0.529
x6 -1.317 1.486 1.148 -1.536 0.227 0.808 0.552 -1.801 -1.440 1.386
x7 -1.267 -1.833 -2.080 3.188 1.531 -0.861 -1.735 4.067 -1.425 0.965
x8 0.021 -0.133 0.762 -0.008 0.041 -0.734 0.068 -0.601 1.066 -0.197

Residuos estandarizados

Para identificar con precisión qué celdas específicas generan la asociación, calculamos los residuos estandarizados (o estudentizados). Estos residuos funcionan como una puntuación Z, permitiéndonos realizar comparaciones estadísticas directas entre celdas:

  • Valores > 1.96: Indican que la frecuencia observada es significativamente superior a la esperada (hay “exceso” de casos en esa celda).
  • Valores < -1.96: Indican que la frecuencia observada es significativamente inferior a la esperada (hay “defecto” de casos en esa celda).

Estos valores nos permiten señalar las celdas que realmente “rompen” la hipótesis de independencia con un nivel de confianza del 95%.

# Displaying rounded results for easier interpretation
kable(round(output.chisq_test$stdres, 3))
hatco A B C D E F G H I
x1 0.775 -0.591 -1.074 1.076 -0.303 0.464 0.229 0.978 -1.293 -0.428
x2 0.221 -0.223 -0.337 0.421 0.476 -0.349 -0.620 0.090 0.222 0.267
x3 -1.140 -1.440 2.619 1.394 1.872 -0.826 -0.931 -1.769 3.282 -1.853
x4 1.393 1.923 -0.007 -2.373 -1.949 0.818 1.497 -1.199 0.114 -0.960
x5 1.225 0.454 -1.234 -1.688 -1.651 0.648 0.666 0.733 -0.400 0.598
x6 -1.491 1.692 1.286 -1.711 0.252 0.922 0.626 -2.032 -1.601 1.567
x7 -1.399 -2.038 -2.273 3.467 1.664 -0.960 -1.922 4.481 -1.546 1.065
x8 0.024 -0.155 0.873 -0.009 0.047 -0.856 0.079 -0.693 1.211 -0.228

Masas y perfiles: La base del cálculo de distancias

Antes de que el algoritmo calcule los ejes de nuestro mapa, el análisis de correspondencias transforma las frecuencias absolutas (los recuentos) en frecuencias relativas.

Para entender de dónde salen las coordenadas, debemos definir tres conceptos estructurales:

  • Masa (Mass): Es la importancia relativa o el “peso” de cada categoría respecto al total de la muestra (porcentaje sobre el gran total). En el mapa, una categoría con mucha masa tendrá mayor capacidad para “tirar” del centro de gravedad hacia sí misma. Tenemos masas de fila (peso de cada atributo) y masas de columna (peso de cada empresa).
  • Perfil de fila (Row profile): Nos muestra cómo se distribuyen las respuestas de un atributo específico entre todas las marcas. Se calcula dividiendo la frecuencia de cada celda entre el total de su fila (es el equivalente al porcentaje de fila). La suma de cada perfil de fila siempre es 1 (o 100%).
  • Perfil de columna (Column profile): Nos indica cómo se compone la valoración de una marca específica a través de todos los atributos. Se calcula dividiendo la celda entre el total de su columna (porcentaje de columna). Si el perfil de columna de una marca es idéntico al perfil medio del mercado (las masas de fila), esa marca se situará exactamente en el centro del mapa (0,0).

En el análisis de correspondencias, la famosa “distancia \(\chi^2\)” no es más que la diferencia matemática entre el perfil de una categoría y el perfil promedio (su masa).

A continuación, extraemos estos cálculos matriciales en R utilizando la función prop.table() sobre nuestra tabla de contingencia original:

# 1. Calculate and display masses (Marginal proportions)
# Row masses: Weight of each attribute
row_masses <- prop.table(margin.table(hatco_table, 1))
cat("--- Row Masses (Attributes) ---\n")
--- Row Masses (Attributes) ---
round(row_masses, 3)
   x1    x2    x3    x4    x5    x6    x7    x8 
0.147 0.145 0.099 0.119 0.123 0.125 0.081 0.162 
# Column masses: Weight of each company
col_masses <- prop.table(margin.table(hatco_table, 2))
cat("\n--- Column Masses (Companies) ---\n")

--- Column Masses (Companies) ---
round(col_masses, 3)
hatco     A     B     C     D     E     F     G     H     I 
0.108 0.120 0.089 0.080 0.079 0.123 0.114 0.103 0.076 0.107 
# 2. Calculate Row Profiles (Sum of each row = 1)
# margin = 1 indicates calculations across rows
row_profiles <- prop.table(hatco_table, margin = 1)
cat("\n--- Row Profiles ---\n")

--- Row Profiles ---
round(row_profiles, 3)
   hatco     A     B     C     D     E     F     G     H     I
x1 0.128 0.104 0.064 0.104 0.072 0.136 0.120 0.128 0.048 0.096
x2 0.114 0.114 0.081 0.089 0.089 0.114 0.098 0.106 0.081 0.114
x3 0.071 0.071 0.167 0.119 0.131 0.095 0.083 0.048 0.167 0.048
x4 0.149 0.178 0.089 0.020 0.030 0.149 0.158 0.069 0.079 0.079
x5 0.143 0.133 0.057 0.038 0.038 0.143 0.133 0.124 0.067 0.124
x6 0.066 0.170 0.123 0.038 0.085 0.151 0.132 0.047 0.038 0.151
x7 0.058 0.043 0.014 0.188 0.130 0.087 0.043 0.261 0.029 0.145
x8 0.109 0.116 0.109 0.080 0.080 0.101 0.116 0.087 0.101 0.101
# 3. Calculate Column Profiles (Sum of each column = 1)
# margin = 2 indicates calculations down columns
col_profiles <- prop.table(hatco_table, margin = 2)
cat("\n--- Column Profiles ---\n")

--- Column Profiles ---
round(col_profiles, 3)
   hatco     A     B     C     D     E     F     G     H     I
x1 0.174 0.127 0.105 0.191 0.134 0.162 0.155 0.182 0.092 0.132
x2 0.152 0.137 0.132 0.162 0.164 0.133 0.124 0.148 0.154 0.154
x3 0.065 0.059 0.184 0.147 0.164 0.076 0.072 0.045 0.215 0.044
x4 0.163 0.176 0.118 0.029 0.045 0.143 0.165 0.080 0.123 0.088
x5 0.163 0.137 0.079 0.059 0.060 0.143 0.144 0.148 0.108 0.143
x6 0.076 0.176 0.171 0.059 0.134 0.152 0.144 0.057 0.062 0.176
x7 0.043 0.029 0.013 0.191 0.134 0.057 0.031 0.205 0.031 0.110
x8 0.163 0.157 0.197 0.162 0.164 0.133 0.165 0.136 0.215 0.154

Estimación del modelo

El paquete ca ofrece una salida de resultados basada en la escuela francesa. Desarrollado por Michael Greenacre, uno de los científicos que más ha desarrollado esta técnica.

# Execute Correspondence Analysis using ca()
ca_greenacre <- ca(hatco_table)

# Display the summary (Inertia, Chi-square, and decomposition)
# This provides the 'Scree Plot' information in table format
summary(ca_greenacre)

Principal inertias (eigenvalues):

 dim    value      %   cum%   scree plot               
 1      0.076541  53.1  53.1  *************            
 2      0.047813  33.2  86.3  ********                 
 3      0.015291  10.6  96.9  ***                      
 4      0.002658   1.8  98.8                           
 5      0.000806   0.6  99.3                           
 6      0.000576   0.4  99.7                           
 7      0.000381   0.3 100.0                           
        -------- -----                                 
 Total: 0.144066 100.0                                 


Rows:
    name   mass  qlt  inr    k=1 cor ctr    k=2 cor ctr  
1 |   x1 |  147  619   41 |  107 289  22 |  115 330  40 |
2 |   x2 |  145  527    8 |   61 469   7 |  -21  58   1 |
3 |   x3 |   99  991  231 |   23   2   1 | -578 989 689 |
4 |   x4 |  119  901  132 | -356 789 196 |  133 111  44 |
5 |   x5 |  123  816   70 | -106 138  18 |  235 677 142 |
6 |   x6 |  125  372  129 | -232 358  87 |   46  14   6 |
7 |   x7 |   81  991  367 |  792 961 665 |  139  30  33 |
8 |   x8 |  162  772   22 |  -43  93   4 | -115 678  45 |

Columns:
     name   mass  qlt  inr    k=1 cor ctr    k=2 cor ctr  
1  | hatc |  108  433   62 | -130 206  24 |  137 228  42 |
2  |    A |  120  928   86 | -283 772 125 |  127 156  40 |
3  |    B |   89  942  115 | -233 294  63 | -346 648 224 |
4  |    C |   80  975  180 |  535 882 299 | -174  93  50 |
5  |    D |   79  863   88 |  268 445  74 | -260 418 111 |
6  |    E |  123  812   29 | -124 456  25 |  110 356  31 |
7  |    F |  114  954   52 | -232 810  80 |   98 144  23 |
8  |    G |  103  963  204 |  465 762 292 |  239 201 123 |
9  |    H |   76  797  128 | -109  49  12 | -425 748 289 |
10 |    I |  107  446   56 |   65  55   6 |  171 390  66 |

Interpretación de la salida summary(ca_greenacre)

La salida de este comando es la “caja negra” del análisis. Nos permite validar si el mapa que estamos viendo es una representación fiel de la realidad o si estamos perdiendo demasiada información.

Bloque de inercia (Principal Inertias)

Esta tabla inicial nos indica cuántas “capas” de información (dimensiones) tiene nuestra tabla y cuánta importancia tiene cada una.

  • dim (Dimension): El número del eje. El primer eje (1) siempre es el que más información captura, el segundo (2) el siguiente, y así sucesivamente.
  • value (Eigenvalue/Inercia): Es el valor propio de la dimensión. Representa la cantidad de varianza (inercia) que ese eje es capaz de explicar de la tabla original.
  • % (Percentage of inertia): Nos da la proporción de la inercia total que captura ese eje. En investigación de mercados, buscamos que los dos primeros ejes sumen un porcentaje elevado (idealmente > 70-80%).
  • cum% (Cumulative percentage): La suma acumulada de los porcentajes anteriores. Nos ayuda a decidir cuántas dimensiones son necesarias para entender el fenómeno.
  • chi2: Es la parte de la estadística Chi-cuadrado asociada a esa dimensión.
  • scree plot: Una representación visual (histograma de puntos) de la importancia relativa de cada eje.

Bloque de filas y columnas (Rows / Columns)

Esta sección es crucial para entender el comportamiento de cada marca o atributo individualmente. Ambos bloques tienen las mismas columnas:

  • mass (Masa): Indica el peso relativo del punto en la muestra total. Una masa de 0.150 significa que esa categoría representa el 15% de los datos. Los puntos con mucha masa tienen más “fuerza” para atraer los ejes hacia ellos.
  • qlt (Quality): Es la calidad de representación (de 0 a 1000). Nos dice qué tan bien representado está ese punto en el mapa actual (usualmente las dos primeras dimensiones).
    • Nota: Si un punto tiene un qlt muy bajo (ej. < 100), su posición en el gráfico es engañosa y no deberías sacar conclusiones estratégicas sobre él.
  • inr (Inertia): Es la contribución del punto a la inercia total de la tabla. Nos dice cuánto “caos” o diferenciación aporta ese punto específico al mercado. Una marca muy distinta al resto tendrá un inr alto.
  • k=1, k=2 (Coordinates): Son las coordenadas exactas del punto en el eje 1 y eje 2. Es lo que R utiliza para dibujar el punto en el mapa cartesiano.
  • cor (Correlation / Cos²): Mide la asociación entre el punto y el eje específico. Un cor alto en el eje 1 significa que ese punto es el que mejor define lo que ese eje representa (ej. el eje del precio).
  • ctr (Contribution): Indica cuánto contribuye ese punto a la construcción de ese eje concreto. Ayuda al investigador a ponerle “nombre” a los ejes (ej. “si las marcas con más ctr en el eje 1 son las más caras, llamaremos al eje 1 ‘Eje de Precio’”).

Cuando analices la tabla, sigue este orden lógico: 1. Mira el % acumulado para saber si tu mapa de 2 dimensiones es suficiente. 2. Observa el qlt de las marcas que te interesan; si es alto, puedes confiar en su posición en el mapa. 3. Usa el ctr para identificar qué variables son las responsables de que el mapa tenga esa forma y no otra.

Visualización

El paquete ca original tiene su propia función plot() que permite un control muy preciso sobre el escalado (simétrico, asimétrico, etc.) de los ejes visuales. Dado que vamos a representar un mapa cartesiano, es importante conocer las coordenadas. Nos abstraemos al cálculo matemático para su obtención.

# Standard Biplot using the ca package's native plotting function
# map = "symmetric" is the standard for market research
kable(ca_greenacre$colcoord)
Dim1 Dim2 Dim3 Dim4 Dim5 Dim6 Dim7
hatco -0.4703327 0.6261240 -1.6935356 0.2946567 -1.7292066 0.4655331 -0.4460250
A -1.0217346 0.5801388 0.5338336 -0.0782325 1.1100906 1.6894406 -1.1173385
B -0.8432000 -1.5832948 0.7964316 0.2620954 0.2874627 -0.1659282 1.3363279
C 1.9333842 -0.7935744 -0.1648613 1.5915023 -0.5015929 1.0023065 0.3955033
D 0.9699347 -1.1891134 1.1762320 0.2415739 -0.3215290 0.1038722 -1.4550986
E -0.4498224 0.5024514 0.2356379 1.0194158 0.2256877 -2.0473172 -0.9157056
F -0.8381141 0.4468266 -0.2145714 0.6596775 0.3110986 0.1309981 1.6885279
G 1.6806871 1.0920696 -0.6758338 -0.7714580 1.5096436 -0.3224750 0.4368324
H -0.3922857 -1.9443407 -1.6117961 -1.8333791 0.3985224 -0.4838085 -0.4346288
I 0.2334232 0.7842522 1.4674729 -1.6200549 -1.4611537 -0.1711086 0.5078109
kable(ca_greenacre$rowcoord)
Dim1 Dim2 Dim3 Dim4 Dim5 Dim6 Dim7
x1 0.3883334 0.5245378 -0.4806489 2.0327480 -0.7993536 -0.3021486 0.5373170
x2 0.2190664 -0.0976211 -0.0513832 -0.4795627 -1.3748826 0.7323587 -1.7893874
x3 0.0836929 -2.6411967 -0.3401945 0.2493870 0.6347806 -1.2085960 -0.3262683
x4 -1.2855444 0.6103704 -0.8976446 0.4026826 1.7762794 0.8684848 -0.7233344
x5 -0.3837837 1.0739489 -0.7367420 -1.3240795 -0.2697071 -1.8311119 0.2870848
x6 -0.8369303 0.2110612 2.4759188 0.1896399 0.1476687 -0.2958822 0.0883928
x7 2.8634622 0.6369024 0.4832047 -0.4465221 1.4975360 0.2144778 -0.0823978
x8 -0.1539424 -0.5248559 -0.2376154 -0.7752912 -0.3939105 1.2342381 1.5910525

Conocidas las coordenadas, representamos el mapa visual.

# Generate the base plot using the ca package with native arrows
plot(ca_greenacre, 
     main = "Mapa simétrico con vectores (paquete ca)",
     map = "symmetric",
     col = c("steelblue", "orange"), 
     col.lab = c("steelblue", "orange"), 
     pch = c(16, 17),
  
     arrows = c(TRUE, TRUE) # TRUE for rows, TRUE for columns
)

Puntos suplementarios

En la investigación estratégica, a menudo queremos proyectar perfiles teóricos (como el “Servicio Ideal” o un “Competidor de Referencia”) sin que estos influyan en la construcción de los ejes. En el paquete ca, esto se gestiona mediante los argumentos supcol (para columnas suplementarias) o suprow (para filas).

Vamos a proyectar nuestro “Competidor IDEAL” para ver qué distancia existe entre la percepción actual de HATCO y la perfección teórica.

# 1. Define the ideal profile (high scores in all attributes)
ideal_profile <- c(15, 15 ,15, 15, 15, 15 ,15, 15)

# 2. Bind the ideal profile as the 11th column of our matrix
hatco_extended <- cbind(hatco_matrix, IDEAL = ideal_profile)

# 3. Estimate the model identifying the 11th column as supplementary
# The algorithm ignores this column for inertia calculation but projects it later
ca_sup <- ca(hatco_extended, supcol = 11)

# 4. Summary of the model including supplementary points
# Note that 'IDEAL' will appear in a separate section of the output
summary(ca_sup)

Principal inertias (eigenvalues):

 dim    value      %   cum%   scree plot               
 1      0.076541  53.1  53.1  *************            
 2      0.047813  33.2  86.3  ********                 
 3      0.015291  10.6  96.9  ***                      
 4      0.002658   1.8  98.8                           
 5      0.000806   0.6  99.3                           
 6      0.000576   0.4  99.7                           
 7      0.000381   0.3 100.0                           
        -------- -----                                 
 Total: 0.144066 100.0                                 


Rows:
    name   mass  qlt  inr    k=1 cor ctr    k=2 cor ctr  
1 |   x1 |  147  619   41 |  107 289  22 |  115 330  40 |
2 |   x2 |  145  527    8 |   61 469   7 |  -21  58   1 |
3 |   x3 |   99  991  231 |   23   2   1 | -578 989 689 |
4 |   x4 |  119  901  132 | -356 789 196 |  133 111  44 |
5 |   x5 |  123  816   70 | -106 138  18 |  235 677 142 |
6 |   x6 |  125  372  129 | -232 358  87 |   46  14   6 |
7 |   x7 |   81  991  367 |  792 961 665 |  139  30  33 |
8 |   x8 |  162  772   22 |  -43  93   4 | -115 678  45 |

Columns:
        name   mass  qlt  inr    k=1 cor  ctr    k=2 cor  ctr  
1  |    hatc |  108  433   62 | -130 206   24 |  137 228   42 |
2  |       A |  120  928   86 | -283 772  125 |  127 156   40 |
3  |       B |   89  942  115 | -233 294   63 | -346 648  224 |
4  |       C |   80  975  180 |  535 882  299 | -174  93   50 |
5  |       D |   79  863   88 |  268 445   74 | -260 418  111 |
6  |       E |  123  812   29 | -124 456   25 |  110 356   31 |
7  |       F |  114  954   52 | -232 810   80 |   98 144   23 |
8  |       G |  103  963  204 |  465 762  292 |  239 201  123 |
9  |       H |   76  797  128 | -109  49   12 | -425 748  289 |
10 |       I |  107  446   56 |   65  55    6 |  171 390   66 |
11 | (*)IDEA | <NA>  289 <NA> |  112 274 <NA> |  -26  15 <NA> |

Visualización del posicionamiento IDEAL

Al graficar, el paquete ca diferencia automáticamente los puntos activos de los suplementarios (generalmente usando símbolos o colores distintos) para que el analista no olvide que ese punto no ha ayudado a “dibujar” el mapa, sino que solo ha sido “invitado” a la foto.

# Plotting with supplementary points
# points = "all" ensures that the supplementary column is displayed
plot(ca_sup, 
     main = "Map with Supplementary Point (Greenacre style)",
     col = c("steelblue", "orange", "seagreen"), 
     col.lab = c("steelblue", "orange"), 
     pch = c(16, 17, 18), # 18 (diamond) for the IDEAL point
     map = "symmetric",
     arrows = c(TRUE,TRUE)
  )

Interpretación estratégica:

Al observar el mapa, el punto IDEAL suele situarse cerca del origen. Esto ocurre porque un perfil que es “bueno en todo” no tiene un sesgo hacia ninguna dimensión específica (no es especialmente “barato” ni especialmente “rápido” comparado con el resto, sino que es máximo en todo).

Para HATCO, la clave no es estar necesariamente cerca del centro (donde el IDEAL se ubica por equilibrio), sino verificar que el ángulo formado entre el vector de HATCO y el del IDEAL sea lo más pequeño posible en las dimensiones que el mercado más valora. ## Decálogo de interpretación del análisis de correspondencias

Al interpretar el mapa cartesiano, aplicamos estas reglas fundamentales:

  1. El origen (0,0): Representa el “perfil promedio” del mercado. Puntos cercanos al centro son poco discriminatorios (no destacan especialmente en nada o son percibidos como la media).
  2. Distancia al origen: Cuanto más lejos está una marca o un atributo del centro, más fuerte es su diferenciación y más contribuye a la inercia del modelo.
  3. Proximidad intra-grupo: La proximidad física entre dos marcas (puntos verdes) indica que los consumidores las perciben como similares (son competidores directos).
  4. Ángulos inter-grupo (Regla de Oro): Para relacionar una marca con un atributo, no miramos solo la distancia física, sino el ángulo que forman al trazar líneas desde el origen (0,0):
    • Ángulo agudo (< 90º): Fuerte asociación positiva. (Ej. HATCO y la Empresa A están muy asociadas a los atributos x4 y x6).
    • Ángulo recto (90º): Independencia. La marca no destaca ni a favor ni en contra de ese atributo.
    • Ángulo obtuso/llano (180º): Oposición o asociación negativa. La marca se define por carecer de ese atributo. (Ej. La Empresa G está diametralmente opuesta al atributo x3).

Diagnóstico estratégico para HATCO

  • El Eje del Prestigio (Dimensión 1 - Derecha): HATCO, A, E y F dominan el cuadrante asociado a la imagen del fabricante (x4), calidad de servicio (x5) e imagen de vendedores (x6). Son las marcas premium. HATCO debe proteger esta reputación.
  • El Eje de Eficiencia de Producto (Dimensión 1 - Izquierda): La empresa G domina en solitario la calidad pura del producto (x7), distanciándose de la “imagen comercial”.
  • El Eje del Precio (Dimensión 2 - Abajo): Las empresas B y H compiten puramente en flexibilidad de precios (x3), siendo las opciones “negociadoras” o económicas del mercado.