MDSConjoint (Marketing Data Science Conjoint) tiene como objetivo mostrar a los estudiantes de grado y posgrado, de marketing avanzado o modelos de comercialización, cómo analizar los datos procedenes de un análisis del conjunto de los atributos de un producto (CA). Esto es, pone énfasis en la estimación de las utilidades parciales, la medida de la importancia de los atributos y la toma de decisiones basadas en datos a partir de la simulación de las cuotas de mercado que cualquier definición del nuevo producto puede objeter en el mercado dados los perfiles de producto existentes en el mercado, la búsqueda del perfi de producto que maximiza la cuota de mercado o el perfil de producto que maximiza el margen de contibución dada la estructura del mercado (Green, Krieger & Wind 2001).
Todo anlálisis conjunto se inicia con la identificación de los atributos y niveles relevantes (mediante reuniones con un grupo de consumidores, por ejemplo), el diseño de un experimento que nos permita estimar las utilidades parciales de los niveles y tal vez algunas interacciones entre atributos relevantes, como la marca y el nivel de precio, aunque todo ello con un número de perfiles de producto reducido. Después se realiza el trabajo de campo (evaluación de los perfiles de producto por parte de los individuos), finalmente se realiza la estimación de las utilidades parciales y la simulación de resultados probables con el objeto de facilitar la toma de decisiones. (ver la documentación del programa SAS sobre marketing)
El objetivo de MDSConjoint es facilitar las tareas estimacion de las utilidades parciales de los niveles de los atributos, la importancia de los atributos, y la simulación de los resultados orientados a la toma de decisiones. Existen otros paquetes en R, como el paquete conjoint, faisalconjoint o support.CEs. Los dos primeros no abordan la toma de decisiones -la optimización de cuota de mercado o del margen de contribución- y no es fácil la estimación de las utilidades parciales de unos datos que no han sido generados por el propio programa. El último, support.CEs, está orientado un una clase de modelos de análisis conjunto denominados modelos de elección discreta o choice base conjoint models (Aizaki 2012).
library(MDSConjoint)El modelo métrico del análisis de conjunto de atributos de un producto (metric conjoint analysis, MCA) se propone medir la utilidad que los diferentes niveles de los atributos de un producto aportan a un individuo o consumidor (Green & Srinivassan 1978). Durante el trabajo de campo los investigadores presentan a los individuos un conjunto de productos definidos por los niveles de los atributos que lo componen, que llamaremos perfiles de producto, y se les pide que valoren la utilidad (o probablidad de compra) que cada perfil de producto les reportaría (en el modelo métrico) o bien que ordene de mayor a menor su preferencia por los productos que le han presentado (modelo no métrico, NMCA).
Para medir la apartación de cada nivel de atributo a la utilidad de cada individuo, estimamos un modelo lineal donde regresamos la utilidad o probabilidad de compra sobre los niveles de los atributos. De hecho cuando todos los atributos son nominales, el modelo lineal es equivalente a un modelo anova de efectos principales (Venables & RIPLEY 2002, chapter 6) con los resultados presentados de cierta manera.
En el modelo MCA los niveles de los atributos toman el papel de variables independientes y las valoraciones que realizan cada uno de los individuos son la variable independiente, la utilidad, los coeficientes estimados nos proporcionan la utilidad parcial con la que cada nivel contribuye a la utilidad (o probabilidad de compra) de cada perfil de producto:
\(U_i = X\beta + \epsilon_i\).
Para facilitar la interpretación de los parámetros del modelo, en la estimación de los coeficientes del modelo, para cada atributo, la suma de los parámetros \(\beta\) se restringe a cero:
\(∑\beta_1j =∑\beta_2j=...=∑\beta_al=0\)
De esa manera, \(\beta_0\) se interpreta como la utilidad media general que aportan los perfiles de producto al individuo (o la probabilidad media de compra), y los parámetros de cada nivel de atributo, que llamaeramos utilidades parciales, indican las variaciones en la utilidad media por el hecho de que el producto incorpore ese nivel en su perfil. La variación máxima en el nivel de utilidad dentro de cada atributo es la aportacion del atributo a la utilidad del individuo. Finalmente, \(\epsilon_i\) es el error del modelo en la predicción de las utilidades de individuo i, esto es, la diferencia entre las utilidades reportadas por el individuos y las que predice el modelo.
Cuando los individuos, en lugar de reportar la utilidad que creen obtener de cada perfil de producto, únicamente ordenan el listado de perfiles de producto, de mayor a menor preferencia, decimos que estamos ante un caso de un modelo no métrico de análisis conjunto, NMCA. En este caso, en lugar de estimar un modelo de regresión ordinario, realizamos una transformación monótona de la ordenación de los perfiles en niveles de utilidades.
\(\gamma(y_i) = X\beta + \epsilon_i\).
Concretamente realizamos la transfromación que proponen en el informe técnico de SAS (SAS, 1993). Debido a la transformación realizada, el ajuste del modelo no métrico siempre será tan bueno o mejor que el ajuste de un modelo métrico con los mismos datos (Young, 1981; Gifi, 1990).
## Loading required package: support.CEs
## Loading required package: DoE.base
## Loading required package: grid
## Loading required package: conf.design
##
## Attaching package: 'DoE.base'
## The following objects are masked from 'package:stats':
##
## aov, lm
## The following object is masked from 'package:graphics':
##
## plot.design
## The following object is masked from 'package:base':
##
## lengths
## Loading required package: MASS
## Loading required package: simex
## Loading required package: RCurl
## Loading required package: bitops
## Loading required package: XML
data("MDSConjointData")
names(MDSConjointData)## [1] "osc" "tire"
tire<-MDSConjointData$tire
tire$design## $Brand
## [1] "Goodstone" "Pirogi" "Machismo"
##
## $Price
## [1] "$69,99" "S79,99" "$89,99"
##
## $Life
## [1] "50000km" "60000km" "70000km"
##
## $Hazard
## [1] "Yes" "No"
Veamos un ejemplo. En este paquete hemos incorporado los que proporciona el programa SAS sobre el diseño de un neumático. Se consideran cuatro atributos: Brand, Price, Life, Hazard. (SAS tire data exemple)
Consideramos tres niveles para el atribudo Brand, (Goodstone, Pirogi, Machismo), tres niveles para el atribudo Price, ($69,99, S79,99, $89,99), tres para la vida media del neumático Life, (50000km, 60000km, 70000km), y dos niveles para la garantía Hazard, (Yes, No)
Para medir los efectos principales de los niveles de los atributos y los efectos de todas las interacciones entre los niveles es necesario que el consumidor valore todas las combinaciones posibles, en este caso: 3x3x3x2=54. Pero es un número muy elevado de perfiles de producto. La función expand.grid() nos proporciona todas las combinaciones posibles entre los niveles de producto.
experiment = expand.grid(
tire$design
)
class(experiment)## [1] "data.frame"
head(experiment)## Brand Price Life Hazard
## 1 Goodstone $69,99 50000km Yes
## 2 Pirogi $69,99 50000km Yes
## 3 Machismo $69,99 50000km Yes
## 4 Goodstone S79,99 50000km Yes
## 5 Pirogi S79,99 50000km Yes
## 6 Machismo S79,99 50000km Yes
tail(experiment)## Brand Price Life Hazard
## 49 Goodstone S79,99 70000km No
## 50 Pirogi S79,99 70000km No
## 51 Machismo S79,99 70000km No
## 52 Goodstone $89,99 70000km No
## 53 Pirogi $89,99 70000km No
## 54 Machismo $89,99 70000km No
length(experiment)## [1] 4
Esto es lo que llamamos el experimento completo, full design. Para reducirlo bsucamos una fracción del experimento completo que tenga la propiedad de que las variables sean independientes, es decir, que su correlazión sea cero.
Por ello normalmente se busca el número de perfiles necesario para poder estimar los efectos principales de los niveles de los atributos y en algunos casos, tambien, las interacciones de primer nivel entre los niveles de algunos atributos, como puede ser la marca y el precio.
Para estimar los efectos principales de los niveles de los atributos en la utilidad del consumidor escogemos los perfiles de producto necesarios para estimarlos de manera que el error sea mínimo una vez elminadas las interacciones que no queremos estimar. Este experimento reducido nos lo proporciona una fracción de perfiles de producto que tienen la propiedad de que las variables son independientes.
Utilizamos la función Lma.design() del paquete support.CEs. La función Lma.desgin() nos proporciona un conjunto ortogonal de perfiles de producto que nos permite estimar los efectos principales de los niveles de los atributos. El resultado lo guardamos en el objeto tire.survey.
# generate a balanced set of product profiles for survey
tire.survey <- Lma.design(attribute.names =
tire$design,
nalternatives = 1, nblocks=1, seed=9999)## The columns of the array have been used in order of appearance.
## For designs with relatively few columns,
## the properties can sometimes be substantially improved
## using option columns with min3 or even min34.
names(tire.survey)## [1] "alternatives" "candidate" "design.information"
tire.survey##
## Choice sets:
## alternative 1 in each choice set
## BLOCK QES ALT Brand Price Life Hazard
## 1 1 1 1 Goodstone $69,99 50000km Yes
## 2 1 2 1 Machismo $89,99 50000km Yes
## 3 1 3 1 Goodstone $69,99 60000km Yes
## 4 1 4 1 Pirogi S79,99 60000km Yes
## 5 1 5 1 Goodstone S79,99 50000km Yes
## 6 1 6 1 Pirogi $89,99 60000km Yes
## 7 1 7 1 Goodstone $89,99 70000km No
## 8 1 8 1 Goodstone S79,99 70000km No
## 9 1 9 1 Machismo $69,99 70000km Yes
## 10 1 10 1 Pirogi $89,99 50000km No
## 11 1 11 1 Pirogi $69,99 50000km No
## 12 1 12 1 Pirogi $69,99 70000km No
## 13 1 13 1 Machismo $89,99 70000km Yes
## 14 1 14 1 Goodstone $89,99 60000km No
## 15 1 15 1 Machismo $69,99 60000km No
## 16 1 16 1 Pirogi S79,99 70000km Yes
## 17 1 17 1 Machismo S79,99 50000km No
## 18 1 18 1 Machismo S79,99 60000km No
##
## Candidate design:
## A B C D
## 1 3 1 2 2
## 2 1 3 3 2
## 3 2 1 1 2
## 4 2 2 2 1
## 5 1 2 3 2
## 6 1 3 2 2
## 7 1 2 1 1
## 8 2 1 3 2
## 9 3 2 2 2
## 10 3 1 3 1
## 11 3 3 3 1
## 12 1 1 1 1
## 13 1 1 2 1
## 14 2 3 1 2
## 15 2 3 2 1
## 16 3 2 1 2
## 17 2 2 3 1
## 18 3 3 1 1
## class=design, type= oa
##
## Design information:
## number of blocks = 1
## number of questions per block = 18
## number of alternatives per choice set = 1
## number of attributes per alternative = 4
El paquete support.CEs, además, nos proportiona un función para elaborar el questionario que utilizaremos para que los consumidores valoren el conjunto de perfiles.
print(questionnaire(tire.survey)) # print survey design for review##
## Block 1
##
## Question 1
## alt.1
## Brand "Goodstone"
## Price "$69,99"
## Life "50000km"
## Hazard "Yes"
##
## Question 2
## alt.1
## Brand "Machismo"
## Price "$89,99"
## Life "50000km"
## Hazard "Yes"
##
## Question 3
## alt.1
## Brand "Goodstone"
## Price "$69,99"
## Life "60000km"
## Hazard "Yes"
##
## Question 4
## alt.1
## Brand "Pirogi"
## Price "S79,99"
## Life "60000km"
## Hazard "Yes"
##
## Question 5
## alt.1
## Brand "Goodstone"
## Price "S79,99"
## Life "50000km"
## Hazard "Yes"
##
## Question 6
## alt.1
## Brand "Pirogi"
## Price "$89,99"
## Life "60000km"
## Hazard "Yes"
##
## Question 7
## alt.1
## Brand "Goodstone"
## Price "$89,99"
## Life "70000km"
## Hazard "No"
##
## Question 8
## alt.1
## Brand "Goodstone"
## Price "S79,99"
## Life "70000km"
## Hazard "No"
##
## Question 9
## alt.1
## Brand "Machismo"
## Price "$69,99"
## Life "70000km"
## Hazard "Yes"
##
## Question 10
## alt.1
## Brand "Pirogi"
## Price "$89,99"
## Life "50000km"
## Hazard "No"
##
## Question 11
## alt.1
## Brand "Pirogi"
## Price "$69,99"
## Life "50000km"
## Hazard "No"
##
## Question 12
## alt.1
## Brand "Pirogi"
## Price "$69,99"
## Life "70000km"
## Hazard "No"
##
## Question 13
## alt.1
## Brand "Machismo"
## Price "$89,99"
## Life "70000km"
## Hazard "Yes"
##
## Question 14
## alt.1
## Brand "Goodstone"
## Price "$89,99"
## Life "60000km"
## Hazard "No"
##
## Question 15
## alt.1
## Brand "Machismo"
## Price "$69,99"
## Life "60000km"
## Hazard "No"
##
## Question 16
## alt.1
## Brand "Pirogi"
## Price "S79,99"
## Life "70000km"
## Hazard "Yes"
##
## Question 17
## alt.1
## Brand "Machismo"
## Price "S79,99"
## Life "50000km"
## Hazard "No"
##
## Question 18
## alt.1
## Brand "Machismo"
## Price "S79,99"
## Life "60000km"
## Hazard "No"
##
## NULL
#sink("questions_for_survey.txt") # send survey to external text file
#questionnaire(tire.survey)
#sink() # send output back to the screenEsos nrows(tire.survey$alternatives) perfiles de producto pueden ser evaluados de diferentes maneras. Los individuos pueden simplemente ordenar la lista de mayor a menor preferencia, de manera que el perfil que está en la primera posición del ranking es el perfil de producto con mayor preferencia. Esta forma de evaluar los perfiles de producto daría lugar a un análisis conjunto no métrico.
Por otro lado, los individuos también pueden evaluar los perfiles en función de la utilidad que creen que les reportaran, repartiendo, pro ejemplo 100 puntos entre los perfiles de producto evaluados.
El programa TRANSREG del paquete de analisis de datos SAS, en cambio, propone evaluar los perfiles en función de la probablidad de compra, que es equivalente a distribuir 100 puntos entre los perfiles de proucto evaluados. (ver)
- For your next tire purchase, how likely are you to buy this product?
Pirogi brand tires at $79.99,
with a 50,000 tread life guarantee,
and without road hazard insurance.Definitely Would Definitely Would
Not Purchase Purchase1 2 3 4 5 6 7 8 9
El modelo de análisis conjunto se estima para cada individuo de la muestra. La función conjoint.estimation() estima un modelo lineal para cada uno de los individuos y reporta el resutlado detallado de la estimación del modelo lineal. Después muestra una tabla de datos resumen de los parámetros estimados para toda la muestra y finalmente una tabla resumen las utilidades parciales de cada uno de los niveles de los atributos incorporados en el modelo así como una tabla con las utilidades (o probablidades) teóricas que predice el modelo. La correlación entre esta última tabla y la tabla con las valoraciones empíricas proporcionadas por los individuos nos da una medida del ajuste medio de las estimaciones.
La función conjoint.estimation(ratings, bundles, design.l) toma como argumentos:
Una base de datos rectangular con las valoraciones de los individuos de los perfiles de producto, que llamamos ratings, con tantas filas como individuos han valorado el conjunto de perfiles de producto, n, y tantas columnas como perfiles de producto hayamos presentado a los individuos, p. Esta tabla de datos tendrá un formato de data frame y una dimensión de n,p.
Una base de datos con los perfiles de productos que han valorado los individuos, que llamamos bundles, con tantas filas como perfiles de producto hayan valorado los individuos, p, y tantas columnas como atributos tengan los perfiles de producto, a. Esta matriz que tendrá un formato de data frame y una dimensión de p,a.
Una lista con los atributos y niveles de los atributos que forman los perfiles valorados. Esta lista que tendrá un formato de list.
Finalmente si las valoraciones de los indiviudos toman la forma de una ordenación de preferencias, entonces rank=1 indica al programa que estamos ante un modelo no métrico y es necesario realizar una transformación de las preferencias en utilidades. Si el modelo es métrico, entonces no es necesario indicar nada.
tires.partWorthsAll<-conjoint.estimation(tire$ratings, tire$bundles, tire$design)
names(tires.partWorthsAll)## [1] "summary" "fit" "part.worths" "prediction"
names(tires.partWorthsAll$summary)## [1] "Subj1" "Subj2" "Subj3" "Subj4" "Subj5"
names(tires.partWorthsAll$summary$Subj2)## [1] "Subj1" "call" "terms" "residuals"
## [5] "coefficients" "aliased" "sigma" "df"
## [9] "r.squared" "adj.r.squared" "fstatistic" "cov.unscaled"
La función conjoint.estimation() nos devuelve un objeto con formato list y cuatro objetos: summary, fit, part.worths, prediction.
El objeto summary, al que se puede acceder con la orden tires.partWorthsAll$Summary, nos proporciona los resutlados detallados del modelo para cada uno de los individuos. Para acceder a ellos sólo es necesario añadir el nombre del individuo a la orden anterior: tires.partWorthsAll$Summary$Subj1 en este ejemplo. Sí únicamente queremos acceder a los coeficientes estimados, tenemos que añadir $coefficients a la orden anterior: tires.partWorthsAll$Summary$Subj1$coefficients. En la tabla siguiente se pueden observar los resutlados para el individuo Subj2:
knitr::kable(tires.partWorthsAll$summary$Subj2$coefficients, digits=2, caption = 'Coeficientes del modelo estimado para el individuos Subj2' )| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 4.94 | 0.11 | 44.50 | 0.00 |
| Brand1 | 1.22 | 0.16 | 7.78 | 0.00 |
| Brand2 | 0.06 | 0.16 | 0.35 | 0.73 |
| Price1 | 2.72 | 0.16 | 17.32 | 0.00 |
| Price2 | -0.11 | 0.16 | -0.71 | 0.50 |
| Life1 | -0.11 | 0.16 | -0.71 | 0.50 |
| Life2 | -0.11 | 0.16 | -0.71 | 0.50 |
| Hazard1 | -0.06 | 0.11 | -0.50 | 0.63 |
Si queremos acceder a todos los demás objetos que forman los resultados del modelo, sólo tenemos que sustituir coefficients por el nombre del resultado que queremos inspeccionar (ver el resultado de Subj1, call, terms, residuals, coefficients, aliased, sigma, df, r.squared, adj.r.squared, fstatistic, cov.unscaled).
La tabla de datos con el resumen de los coeficientes estimados para toda la muestra se muestran en el objeto fit. Para acceder sólo tenemos que utilizar la orden siguiente: tires.partWorthsAll$fit. Nos proporciona una base de datos con tantas filas como individuos han valorado el conjunto de perfiles de producto y tantas columnas como niveles de los atributos menos uno. En este ejemplo, tendríamos (3-1)+(3-1)+(3-1)+(2-1)+1=8. Si no queremos ver toda la base de datos podemos inspeccionar su inicio y final con las funciones head() y tail(), y en entre paréntesis sólo tenemos que poner el nombre del objeto, tires.partWorthsAll$fit en este caso. Utilizamos la función kable() para dar formato a la tabla de datos.
| (Intercept) | Brand1 | Brand2 | Price1 | Price2 | Life1 | Life2 | Hazard1 | |
|---|---|---|---|---|---|---|---|---|
| Subj1 | 5.11 | -0.11 | -0.61 | 2.56 | -0.11 | -1.28 | 0.22 | -0.33 |
| Subj2 | 4.94 | 1.22 | 0.06 | 2.72 | -0.11 | -0.11 | -0.11 | -0.06 |
| Subj3 | 5.00 | -0.17 | -0.50 | 1.00 | -0.33 | -2.83 | 0.17 | -0.22 |
| Subj4 | 5.11 | 0.72 | 0.39 | 0.72 | -0.11 | -2.61 | 0.06 | -0.44 |
| Subj5 | 5.06 | -0.06 | -0.39 | 2.61 | -0.06 | -1.39 | 0.11 | -0.17 |
Para completar los resultados de las estimaciones, la funcion conjoint.estimation() nos proporciona una base de datos con las utilidades parciales de cada uno de los niveles, incluidos los que se han eliminado para poder estimar el modelo (si no los hubieramos eliminados, el modelo no se podría haber estimado). La tabla de datos tiene tantas finals como perfiles de producto, p, y tantas columnas como niveles de atributos hemos considerado en el análisis: 3+3+3+2=11 en este caso de los neumáticos. Esta forma de mostrar las utilidades parciales de cada nivel nos facilita el cálculo de la importancia de cada atributo como veremos después. De nuevo utilizamos la función kable() para dar formato a la tabla de datos.
| Goodstone | Pirogi | Machismo | $69,99 | S79,99 | $89,99 | 50000km | 60000km | 70000km | Yes | No | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Subj1 | -0.11 | -0.61 | 0.72 | 2.56 | -0.11 | -2.44 | -1.28 | 0.22 | 1.06 | -0.33 | 0.33 |
| Subj2 | 1.22 | 0.06 | -1.28 | 2.72 | -0.11 | -2.61 | -0.11 | -0.11 | 0.22 | -0.06 | 0.06 |
| Subj3 | -0.17 | -0.50 | 0.67 | 1.00 | -0.33 | -0.67 | -2.83 | 0.17 | 2.67 | -0.22 | 0.22 |
| Subj4 | 0.72 | 0.39 | -1.11 | 0.72 | -0.11 | -0.61 | -2.61 | 0.06 | 2.56 | -0.44 | 0.44 |
| Subj5 | -0.06 | -0.39 | 0.44 | 2.61 | -0.06 | -2.56 | -1.39 | 0.11 | 1.28 | -0.17 | 0.17 |
Finalmente la tabla de datos con las predicciones teóricas de la utilidad que reportan los perfiles de producto para toda la muestra se pueden obtener con el objeto prediction. Para acceder sólo tenemos que utilizar la orden siguiente: tires.partWorthsAll$prediction. Nos proporciona una tabl de datos con tantas filas como individuos han valorado el conjunto de perfiles de producto, n, y tantas columnas como niveles com perfiles han valorado, p. Si no queremos ver toda la base de datos podemos inspeccionar su inicio y final con las funciones head() y tail(), y en entre paréntesis sólo tenemos que poner el nombre del objeto, tires.partWorthsAll$prediction en este caso. Utilizamos la función kable() para dar formato a la tabla de datos.
| Subj1 | Subj2 | Subj3 | Subj4 | Subj5 | |
|---|---|---|---|---|---|
| bundle1 | 2.78 | 4.72 | 1.11 | 2.33 | 3.06 |
| bundle2 | 5.94 | 8.72 | 2.78 | 3.50 | 6.06 |
| bundle3 | 6.44 | 3.72 | 7.78 | 6.00 | 6.56 |
| bundle4 | 3.94 | 3.83 | 7.06 | 8.22 | 3.89 |
| bundle5 | 6.44 | 3.72 | 7.78 | 6.00 | 6.56 |
| bundle6 | 8.44 | 8.00 | 8.39 | 9.22 | 8.72 |
| ## Estimac | ión del | modelo ú | nicament | e para u | n individuo |
La función conjoint.estimation() también se puede utilizar para estimar el modelo sólo para un individuo o para un grupo de invidiuos. Esta posibilidad es interesante para valorar las utilidades parciales de un determinado segmento. Sólo tenemos que indicar que únicamente queremos que estime el modelo para un indiviuo en concreto o para un grupo de invidividuos, por ejemplo para el primer individuo de la muestra. Para ello utilizamos los corchetes y dentro de ellos indicamos la fila en la que está el individuo y una coma que indicada que utilizaremos todas las columnas de la base de datos: [1,].
La estimación del modelo con devuelve los mismos objetos: una lista con los resultados detallados, una tabla de datos con los coeficientes de la estimación, otra lista de datos con las utilidades parciales de cada nivel de atributo para el individuo o el subconjunto de invidiuos, y una tabla de datos con las predicciones del modelo, o utilidades teóricas.
tires.partWorths1<-conjoint.estimation(tire$ratings[1,], tire$bundles, tire$design)
names(tires.partWorths1)## [1] "summary" "fit" "part.worths" "prediction"
names(tires.partWorths1$summary$Subj1)## [1] "call" "terms" "residuals" "coefficients"
## [5] "aliased" "sigma" "df" "r.squared"
## [9] "adj.r.squared" "fstatistic" "cov.unscaled"
knitr::kable(tires.partWorths1$summary$Subj1$coefficients, digits=2, caption = 'Coeficientes del modelo estimado para el individuos Subj1' )| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 5.11 | 0.12 | 43.86 | 0.00 |
| Brand1 | -0.11 | 0.16 | -0.67 | 0.52 |
| Brand2 | -0.61 | 0.16 | -3.71 | 0.00 |
| Price1 | 2.56 | 0.16 | 15.51 | 0.00 |
| Price2 | -0.11 | 0.16 | -0.67 | 0.52 |
| Life1 | -1.28 | 0.16 | -7.75 | 0.00 |
| Life2 | 0.22 | 0.16 | 1.35 | 0.21 |
| Hazard1 | -0.33 | 0.12 | -2.86 | 0.02 |
knitr::kable(head(tires.partWorths1$fit), digits=2, caption = 'Estimaciones: Resultados de *j* individuos' )| (Intercept) | Brand1 | Brand2 | Price1 | Price2 | Life1 | Life2 | Hazard1 | |
|---|---|---|---|---|---|---|---|---|
| Subj1 | 5.11 | -0.11 | -0.61 | 2.56 | -0.11 | -1.28 | 0.22 | -0.33 |
knitr::kable(head(tires.partWorths1$part.worths), digits=2, caption = 'Utilidades parciales: Resultados de *j* individuos' )| Goodstone | Pirogi | Machismo | $69,99 | S79,99 | $89,99 | 50000km | 60000km | 70000km | Yes | No | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Subj1 | -0.11 | -0.61 | 0.72 | 2.56 | -0.11 | -2.44 | -1.28 | 0.22 | 1.06 | -0.33 | 0.33 |
La función importance.of.attributes() nos proporciona tres tablas de datos. La primera es el resumen de los coeficientes estimados, fit, la segunda es la tabla de las utilidades parciales, part.worths, y la tercera la tabla resumen de la importancia de los atributos para los individuos de la muestra, imp.
tires.imp <- importance.of.attributes(tire$ratings, tire$bundles, tire$design)
names(tires.imp)## [1] "fit" "part.worths" "imp"
En este momento nos interesan las dos últimas tablas. La tabla part-worths y la tabla imp.
| Goodstone | Pirogi | Machismo | $69,99 | S79,99 | $89,99 | 50000km | 60000km | 70000km | Yes | No | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Subj1 | -0.11 | -0.61 | 0.72 | 2.56 | -0.11 | -2.44 | -1.28 | 0.22 | 1.06 | -0.33 | 0.33 |
| Subj2 | 1.22 | 0.06 | -1.28 | 2.72 | -0.11 | -2.61 | -0.11 | -0.11 | 0.22 | -0.06 | 0.06 |
| Subj3 | -0.17 | -0.50 | 0.67 | 1.00 | -0.33 | -0.67 | -2.83 | 0.17 | 2.67 | -0.22 | 0.22 |
| Subj4 | 0.72 | 0.39 | -1.11 | 0.72 | -0.11 | -0.61 | -2.61 | 0.06 | 2.56 | -0.44 | 0.44 |
| Subj5 | -0.06 | -0.39 | 0.44 | 2.61 | -0.06 | -2.56 | -1.39 | 0.11 | 1.28 | -0.17 | 0.17 |
La primera tabla nos permite calcular la importancia de los atributos como la diferencia en el valor mínimo y el máximo de las utilidades parciales reportadas por los niveles de cada atributo. La tabla imp nos proporciona el resumen para todos los individuos, en porcentaje.
knitr::kable(head(tires.imp$imp),digits=2, caption = 'Importancia de los atributos: Resultados de los 5 primeros individuos' )| Brand | Price | Life | Hazard | |
|---|---|---|---|---|
| Subj1 | 14.29 | 53.57 | 25.00 | 7.14 |
| Subj2 | 30.20 | 64.43 | 4.03 | 1.34 |
| Subj3 | 13.29 | 18.99 | 62.66 | 5.06 |
| Subj4 | 19.88 | 14.46 | 56.02 | 9.64 |
| Subj5 | 9.26 | 57.41 | 29.63 | 3.70 |
Finalmente si queremos conocer la importancia media para toda la muestra, sólo tenemos que calcular la media de las columnas de la tabla imp. Para ello utilizamos la función apply() y como armentos la tabla de datos tires.imp$imp, el número 2 que indica que queremos trabajr con las columnas de la tabla, y la función mean para indicar que queremos calcular la media de las columnas. Utilizamos la función kable() para dar formato a la tabla.
mean(tires.imp$imp$Brand)## [1] 17.38339
class(tires.imp$imp$Brand)## [1] "numeric"
knitr::kable(apply(tires.imp$imp, 2, mean),digits=2, caption = 'Importancia media de los atributos' )| Brand | 17.38 |
| Price | 41.77 |
| Life | 35.47 |
| Hazard | 5.38 |
Finalmente podemos visualizar la importancia media de los atributos con la función visualize.importance que necesita como argumentos la tabla de datos con las utilidades parciales, tires.imp$part.worths, la tabla de datos con las importancias en procentajes, tires.imp$imp, y la lista con los nombre de los atributos y niveles, tires$design.
visualize.importance(tires.imp$part.worths,tires.imp$imp, tire$design)El paquete MDSConjoiont también nos proporciona un conjunto de funciones para tomar decisiones con los resultados del análsis conjunto. Concretamente podemos simular las cuotas de mercado que ciertos perfiles de producto obtedrían en el mercado, e identificar el perfil de producto que maximizaría la cuota de mercado de la empresa, dados los perfiles de producto existentes en el mercado. Veámoslo con el ejemplo sintético de Office System que proporciona el libro Marketing Engineering (Lilien & Ramaswamy 2003)
This artificial data is about the design of a office store, the kind of of products to offer, office supplies, forniture, and computers and software, and the location of the store. The levels of each attribute can be browsed with the design object of the list osc.
Primero seleccionaremos los datos del ejemplo Office System asignándolos al objeto osc, y después obtenemos los datos del diseño.
osc<-MDSConjointData$osc
osc$design## $Location
## [1] "Less2Miles" "W2-5Miles" "W5-10Miles"
##
## $OfficeSupplies
## [1] "VLAssortment" "LAssortment" "LimAssortment"
##
## $Furniture
## [1] "Yes" "No"
##
## $Computers
## [1] "NoComputers" "Software" "SoftwareAndComputers"
Seguidamente estimaremos el modelo para todos los individuos de la muestra utilizando la función conjoint.estimation() y como argumentos las valoraciones de los individuos, osc$ratings, la descripción de los perfiles de producto valorados, osc$bundles, y la descripción de los atributos y niveles, osc$design.
osc.partWorthsAll<-conjoint.estimation(osc$ratings, osc$bundles, osc$design)
class(osc.partWorthsAll)## [1] "list"
names(osc.partWorthsAll)## [1] "summary" "fit" "part.worths" "prediction"
names(osc.partWorthsAll$summary)## [1] "Respondent1" "Respondent2" "Respondent3" "Respondent4"
## [5] "Respondent5" "Respondent6" "Respondent7" "Respondent8"
## [9] "Respondent9" "Respondent10" "Respondent11" "Respondent12"
## [13] "Respondent13" "Respondent14" "Respondent15" "Respondent16"
## [17] "Respondent17" "Respondent18" "Respondent19" "Respondent20"
names(osc.partWorthsAll$summary$Respondent1)## [1] "call" "terms" "residuals" "coefficients"
## [5] "aliased" "sigma" "df" "r.squared"
## [9] "adj.r.squared" "fstatistic" "cov.unscaled"
Ahora vamos a observar los resultados para el individuos número 1, Respondent1, utilizando la orden osc.partWorthsAll$summary$Respondent1. Para inspeccionar únicamente los coeficientes estimados utiizamos la orden osc.partWorthsAll$summary$Respondent1$coefficients. Los resultados los podemos ver en la siguiente tabla.
knitr::kable(osc.partWorthsAll$summary$Respondent1$coefficients, digits=2, caption = 'Estimación del modelo lineal: coeficientes del primer individuo' )| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 58.64 | 1.63 | 35.93 | 0.00 |
| Location1 | 9.34 | 2.26 | 4.13 | 0.00 |
| Location2 | 3.81 | 1.95 | 1.96 | 0.09 |
| OfficeSupplies1 | 1.05 | 2.02 | 0.52 | 0.62 |
| OfficeSupplies2 | -1.35 | 2.23 | -0.60 | 0.56 |
| Furniture1 | -20.36 | 1.46 | -13.94 | 0.00 |
| Computers1 | -2.35 | 2.28 | -1.03 | 0.33 |
| Computers2 | -2.98 | 1.95 | -1.53 | 0.16 |
En esta tabla podemos ver el inicio de la tabla de datos con las estimaciones de los 6 primeros individuos de la muestra,
| Respondent1 | Respondent2 | Respondent3 | Respondent4 | Respondent5 | Respondent6 | |
|---|---|---|---|---|---|---|
| (Intercept) | 58.64 | 53.85 | 58.56 | 70.71 | 70.86 | 63.24 |
| Location1 | 9.34 | 11.74 | 5.28 | 0.07 | 6.58 | 15.45 |
| Location2 | 3.81 | 0.27 | -3.05 | 3.62 | -1.92 | -3.41 |
| OfficeSupplies1 | 1.05 | 2.90 | 1.59 | -0.32 | 3.29 | 0.14 |
| OfficeSupplies2 | -1.35 | -2.60 | 0.19 | -10.92 | -14.61 | -6.16 |
| Furniture1 | -20.36 | -0.82 | -1.46 | -1.46 | 0.81 | -6.82 |
| Computers1 | -2.35 | -15.89 | -20.70 | -6.54 | 1.29 | 1.32 |
| Computers2 | -2.98 | -15.27 | -6.95 | 2.21 | -3.08 | 0.07 |
El tercer objeto que nos proporciona la función conjoint.estimation() es la tabla de datos con las utilidades parciales de los 6 primeros individuos si utilizamos la función head().
| Respondent1 | Respondent2 | Respondent3 | Respondent4 | Respondent5 | Respondent6 | |
|---|---|---|---|---|---|---|
| Less2Miles | 9.34 | 11.74 | 5.28 | 0.07 | 6.58 | 15.45 |
| W2-5Miles | 3.81 | 0.27 | -3.05 | 3.62 | -1.92 | -3.41 |
| W5-10Miles | -13.16 | -12.01 | -2.22 | -3.69 | -4.66 | -12.05 |
| VLAssortment | 1.05 | 2.90 | 1.59 | -0.32 | 3.29 | 0.14 |
| LAssortment | -1.35 | -2.60 | 0.19 | -10.92 | -14.61 | -6.16 |
| LimAssortment | 0.29 | -0.30 | -1.77 | 11.24 | 11.33 | 6.02 |
| Yes | -20.36 | -0.82 | -1.46 | -1.46 | 0.81 | -6.82 |
| No | 20.36 | 0.82 | 1.46 | 1.46 | -0.81 | 6.82 |
| NoComputers | -2.35 | -15.89 | -20.70 | -6.54 | 1.29 | 1.32 |
| Software | -2.98 | -15.27 | -6.95 | 2.21 | -3.08 | 0.07 |
| SoftwareAndComputers | 5.34 | 31.16 | 27.64 | 4.32 | 1.78 | -1.40 |
El cuarto objeto que nos proporciona la funicón es la tabla resumen de las predicciones teóricas del modelo, prediction. La correlación entre las utilidades teóricas proporcionadas por el modelo y la empíricas proporcionadas por los individuos nos da una medida sintética de la bondad del ajuste del modelo.
Como en el ejemplo de los neumáticos, la función importance.of.attributes() nos proporciona tres tablas de datos, una con el resumen de los coeficientes estimados, fit, otra con las utilidades parciales, part.worths, y la tercera con la importancia de cada atributo para cada individuo, en porcentje.
Con la función kable()del paquete knitr podemos presentarlos en una tabla bien formateada.
osc.imp <- importance.of.attributes(osc$ratings, osc$bundles, osc$design)
names(osc.imp)## [1] "fit" "part.worths" "imp"
knitr::kable(head(osc.imp$imp),digits=2, caption = 'Importancia de los atributos: Resultados de los 5 primeros individuos' )| Location | OfficeSupplies | Furniture | Computers | |
|---|---|---|---|---|
| Respondent1 | 30.43 | 3.25 | 55.08 | 11.25 |
| Respondent2 | 30.47 | 7.06 | 2.12 | 60.36 |
| Respondent3 | 13.23 | 5.34 | 4.64 | 76.79 |
| Respondent4 | 16.89 | 51.23 | 6.77 | 25.11 |
| Respondent5 | 25.75 | 59.38 | 3.73 | 11.14 |
| Respondent6 | 49.07 | 21.73 | 24.34 | 4.85 |
Para obtener la importancia media de cada uno de los atributos que dan forma a los perfiles de producto valorados, sólo tenemos que calcular la media de las columnas de la tabla imp. Para ello utilizamos la función apply() y como armentos la tabla de datos tires.imp$imp, el número 2que indica que queremos trabajr con las columnas de la tabla, y la función mean para decir que queremos calcular la media de las columnas. Utilizamos la función kable() para dar formato a la tabla.
mean(osc.imp$imp$Location)## [1] 27.29945
class(osc.imp$imp$Location)## [1] "numeric"
knitr::kable(apply(osc.imp$imp, 2, mean), digits=2, caption = 'Resumen importancia atributos' )| Location | 27.30 |
| OfficeSupplies | 25.15 |
| Furniture | 16.47 |
| Computers | 31.08 |
Finalmente podemos visualizar la importancia media de los atributos con la función visualize.importance() que necesita como argumentos la tabla de datos con las utilidades parciales, osc.imp$part.worths, la tabla de datos con las importancias en procentajes, osc.imp$imp, y la lista con los nombres de los atributos y niveles, osc$design.
visualize.importance(osc.imp$part.worths, osc.imp$imp, osc$design)Para simular las cuotas de mercado necesitamos información acerca de los perfiles de producto de la competencia existentes en el mercado, osc$market.profiles en este caso.
dim(osc$market.profiles)## [1] 2 4
knitr::kable(osc$market.profiles, digits=2, caption = 'Market profiles')| Location | OfficeSupplies | Furniture | Computers | |
|---|---|---|---|---|
| OfficeEquipment | W2-5Miles | LAssortment | Yes | SoftwareAndComputers |
| DepartmentStore | W2-5Miles | LimAssortment | No | Software |
También necesitamos la tabla de datos con la descripción de los perfiles de producto valorados por los individuos, osc$bundles en este caso.
knitr::kable(osc$bundles, digits=2, caption = 'Bundles of attributes to be rated')| Location | OfficeSupplies | Furniture | Computers | |
|---|---|---|---|---|
| Bundle1 | Less2Miles | VLAssortment | Yes | NoComputers |
| Bundle2 | Less2Miles | LAssortment | No | Software |
| Bundle3 | Less2Miles | LimAssortment | No | SoftwareAndComputers |
| Bundle4 | Less2Miles | LAssortment | Yes | Software |
| Bundle5 | W2-5Miles | VLAssortment | Yes | Software |
| Bundle6 | W2-5Miles | LAssortment | No | NoComputers |
| Bundle7 | W2-5Miles | LimAssortment | No | Software |
| Bundle8 | W2-5Miles | LAssortment | Yes | SoftwareAndComputers |
| Bundle9 | W5-10Miles | VLAssortment | No | SoftwareAndComputers |
| Bundle10 | W5-10Miles | LAssortment | Yes | Software |
| Bundle11 | W5-10Miles | LimAssortment | Yes | NoComputers |
| Bundle12 | W5-10Miles | LAssortment | No | Software |
| Bundle13 | W2-5Miles | VLAssortment | No | Software |
| Bundle14 | W2-5Miles | VLAssortment | Yes | SoftwareAndComputers |
| Bundle15 | W2-5Miles | LimAssortment | Yes | Software |
| Bundle16 | W2-5Miles | LAssortment | No | NoComputers |
Para completar los datos necesitamos también la valoración que han realizado los consumidores de los perfiles de productos, osc$ratings.
knitr::kable(t(head(osc$ratings)), digits=2, caption = 'Ratings of bundles by individulas')| Respondent1 | Respondent2 | Respondent3 | Respondent4 | Respondent5 | Respondent6 | |
|---|---|---|---|---|---|---|
| Bundle1 | 90 | 50 | 40 | 75 | 90 | 95 |
| Bundle2 | 50 | 55 | 60 | 80 | 80 | 70 |
| Bundle3 | 50 | 95 | 90 | 60 | 70 | 70 |
| Bundle4 | 80 | 50 | 60 | 70 | 70 | 80 |
| Bundle5 | 85 | 50 | 45 | 90 | 80 | 80 |
| Bundle6 | 40 | 40 | 35 | 65 | 75 | 50 |
| Bundle7 | 40 | 40 | 45 | 60 | 50 | 50 |
| Bundle8 | 90 | 85 | 85 | 85 | 75 | 70 |
| Bundle9 | 30 | 75 | 80 | 85 | 80 | 40 |
| Bundle10 | 60 | 35 | 55 | 70 | 75 | 60 |
| Bundle11 | 60 | 20 | 40 | 55 | 50 | 55 |
| Bundle12 | 30 | 25 | 45 | 60 | 60 | 50 |
| Bundle13 | 30 | 25 | 50 | 90 | 80 | 60 |
| Bundle14 | 90 | 85 | 85 | 85 | 75 | 70 |
| Bundle15 | 80 | 35 | 50 | 70 | 50 | 50 |
| Bundle16 | 40 | 45 | 35 | 65 | 75 | 55 |
La función utilities.of.profiles() nos predice la utilidad que obtedría cada uno de los perfiles de producto de la competencia existene en el mercado. Esta función proporciona una tabla de datos con tantas filas como individuos tenemos en la muestra, n, y tantas columnas como marcas, m, tenemos en el objeto osc$market.profiles.
dim(utilities.of.profiles(osc$market.profiles, osc$ratings, osc$bundles))## [1] 20 2
knitr::kable(head(utilities.of.profiles(osc$market.profiles, osc$ratings, osc$bundles)), digits=2, caption = 'Ratings of bundles by individulas')| OfficeEquipment | DepartmentStore | |
|---|---|---|
| Respondent1 | 89.2 | 37.77 |
| Respondent2 | 89.0 | 35.42 |
| Respondent3 | 86.2 | 47.29 |
| Respondent4 | 79.8 | 64.16 |
| Respondent5 | 73.2 | 52.06 |
| Respondent6 | 65.4 | 46.93 |
Para predecir las cuotas de mercado primero tenemos que decidir qué regla de elección con mayor probabilidad utilizarán los consumidores a la hora de realizar sus elecciones: la regla de la máxima utilidad (primera elección), la regla de la cuota de preferencia (también denominada BTL), y la regla logit. Para el primer caso disponemos de la función ms.fe.conjoint(), para le segundo, ms.us.conjoint(), y para el tercero, ms.logit.conjoint() (Green & Krieger 1995).
Máxima utilidad (1ª elección): \(p_i(J_k)= 1, si U(J_k)=max(U(J_k))\), si no \(p_i(J_k)= 0\)
Cuota de utilidad (BTL): \(p_i(J_k)= U(J_k)/∑U(J_k)\)
Regla logit: \(p_i(J_k)= exp(U(J_k))/∑exp(U(J_k))\)
Las tres funciones necesitan como argumento tres tablas de datos, la tabla con los perfiles de mercado para los que vamos a simular las cuotas de mercado, osc$market.profiles, la base de datos con las valoraciones realizadas por los individios, osc$ratings, y la base de datos con la descripción de los perfiles de producto valorados, osc$bundles, en este caso.
En el caso de la máxima utilidad o primera elección la cuota de mercado para las dos marcas existentes en el mercado es:
knitr::kable(ms.fe.conjoint(osc$market.profiles, osc$ratings, osc$bundles), digits=2, caption = 'Regla de la maxima utildad')| ms | |
|---|---|
| OfficeEquipment | 100 |
| DepartmentStore | 0 |
ms.fe.conjoint(osc$market.profiles, osc$ratings, osc$bundles)## ms
## OfficeEquipment 100
## DepartmentStore 0
Si los individuos utilizaran la regla de la cuota de preferencia, las cuotas serían:
knitr::kable(ms.us.conjoint(osc$market.profiles, osc$ratings, osc$bundles), digits=2, caption = 'Regla de la cuota de preferencia')| ms | |
|---|---|
| OfficeEquipment | 62 |
| DepartmentStore | 38 |
Si los individuos utilizaran la regla logit, las cuotas estimadas serían:
knitr::kable(ms.logit.conjoint(osc$market.profiles, osc$ratings, osc$bundles), digits=2, caption = 'Regla logit')| ms | |
|---|---|
| OfficeEquipment | 100 |
| DepartmentStore | 0 |
Si estamos interesados en identificar el perfil que maximiza la cuota de mercado de la empresa dados los perfiles de producto de la competencia, disponemos de las funciones optim.ms.first.choice(), optim.ms.utility.share() y optim.ms.logit() que utilizaremos en función de la regla de decisión que utilicen los individuos.
En todos los casos, la función necesita como argumentos las valoraciones de los consumidores, osc$ratings, la descripción de los perfiles valorados, osc$bundles, los perfiles de producto de la competencia, osc$market.profiles, la descripción de todos los posibles perfiles de producto, osc$full, y el argumento hide progress bar, hpb=1, en el caso de que no queramos ver la barra de progreso (la identificación del óptimo puede tardar un poco dependiendo de la capacidad de proceso del ordenador). Este último argumento es interesante cuando utilizamos la función en un documento rmarkdown.
La función nos devuelve una lista con dos objetos. El primero nos muestra la descripción del perfil óptimo y el segundo la cuota de mercado que obtendría la competencia y el producto que maximiza la cuota de mercado.
osc.ms.op.1choice<-optim.ms.first.choice(osc$ratings, osc$bundles, osc$market.profiles, osc$design, hpb=1)
knitr::kable(osc.ms.op.1choice, digits=2, caption = 'Regla máxima utilidad')
|
|
osc.ms.op.logit<-optim.ms.logit(osc$ratings, osc$bundles, osc$market.profiles, osc$design, hpb=1)
knitr::kable(osc.ms.op.logit, digits=2, caption = 'Regla de la cuota de preferencia')
|
|
Esta primer versión del paquete MDSConjoint nos proporciona herramientas para facilitar la toma de decisiones, la simulación de la cuota de mercado de cualquier perfil de producto, dada la competencia, la obtención del perfiil que maximiza la cuota de mercado o el margen de contribución (Green, Carroll, & Goldberg, 1981; Green & Krieger, 1995).
Futuras versiones introducirán algoritmos basados en programación dinámica con el objeto de utilizar algoritmos más eficiente en la obtención del producto óptimo (Kohli & Krishnamurthy 1989). Otras mejoras están orientadas a incrementar la funcionalidad del paquete en la toma de decisiones comerciales, como la configuración de una línea de productos (Green & Krieger 1992; Michalek, Ebbes, Adigüzel, Feinberg, and Papalambros 2011), la simulación de de cuotas de mercado para niveles de producto, como el precio, que no se han considerado expresamente en el diseño del análisis conjunto (Pekelman & Sen 1979), segmentación de mercados según las utilidades parciales (Green & Krieger 1991), o las decisiones de precios (Kohli & Mahajan 1991), etc.
Aizaki, H. (2012). Basic Functions for Supporting an Implementation of Choice Experiments in R | Aizaki | J. Journal of Statistical Software, 50. Retrieved from https://www.jstatsoft.org/article/view/v050c02
Green, P. E., & Krieger, A. M. (1995). Attribute importance weights modification in assessing a Brand’s competitive potential. Marketing Science, 14(3), 253–270.
Green, P. E., & Krieger, A. M. (1988). Choice Rules and Sensitivity Analysis in Conjoint Simulators. Journal of the Academy of Marketing Science, 16(1), 114–127. http://doi.org/10.1177/009207038801600110
Green, P. E., & Srinivasan, V. (1978). Conjoint Analysis in Consumer Research: Issues and Outlook. Journal of Consumer Research, 5(September), 103–123.
Green, P. E., Carroll, J. D., & Goldberg, S. M. (1981). A General Approach to Porducht Design Optimization via Conjoint Analysis. Journal of Marketing, 45(Summer), 17–37.
Green, P. E., & Krieger, A. M. (1992). An application of a product positioning model to pharmaceutical products. MARKETING SCIENCE, 11(2).
Green, P. E., Krieger, A. M., & Wind, Y. (2001). Thirty Years of Conjoint Analysis: Reflections and Prospects. Interfaces, 31(3_supplement), S56–S73. http://doi.org/10.1287/inte.31.3s.56.9676
Green, P. E., & Krieger, A. M. (1991). Segmenting Markets with Conjoint Analysis. Journal of Marketing, 55(4), 20–31. http://doi.org/10.2307/1251954
Kohli, R., & Mahajan, V. (1991). A Reservation-Price Model for Optimal Pricing of Multiattribute Products in Conjoint Analysis. Journal of Marketing Research, 28(August), 347–354.
Kohli, R., & Krishnamurthy, R. (1989). Optimal product design using conjoint analysis: Computational complexity and algorithms. European Journal of Operational Research, 40, 186–195.
Lilien, Gary L., and Arvind Rangaswamy. Marketing Engineering: Computer-Assisted Marketing Analysis and Planning by Gary L. Lilien. Pearson, 2003.
Michalek, Jeremy J., Peter Ebbes, Feray Adigüzel, Fred M. Feinberg, and Panos Y. Papalambros. “Enhancing Marketing with Engineering: Optimal Product Line Design for Heterogeneous Markets☆☆☆.” International Journal of Research in Marketing, January 2011. doi:10.1016/j.ijresmar.2010.08.001.
Pekelman, D., & Sen, S. K. (1979). Improving Prediction in Conjoint Measurement. Journal of Marketing Research, 16(2), 211–220. http://doi.org/10.2307/3150685
SAS Institute Inc.(1993), SAS TeclmicalReport R-109, Conjoint Analysis Examples, Gary, NC: SAS Institute Inc.,85 pp.
El paquete XLConnect y junto con el complemento XLConnectJars nos proporcionan la posibilidad de leer libros de hojas electrónicas en formalo xlsx.
require(XLConnect)## Loading required package: XLConnect
## Loading required package: XLConnectJars
## XLConnect 0.2-12 by Mirai Solutions GmbH [aut],
## Martin Studer [cre],
## The Apache Software Foundation [ctb, cph] (Apache POI, Apache Commons
## Codec),
## Stephen Colebourne [ctb, cph] (Joda-Time Java library),
## Graph Builder [ctb, cph] (Curvesapi Java library)
## http://www.mirai-solutions.com ,
## http://miraisolutions.wordpress.com
require(XLConnectJars)La función readWorksheet() nos permite leer un libro de hojas electrónicas, sas-conjoint.xlsx por ejemplo, y guardarlo en el objeto tireData, por ejemplo.
tireData <- loadWorkbook("sas-conjoint.xlsx", create = T) #loading the spreadshit bookDespués podemos ir leyendo las diferentes hojas que nos interesan: ratings, design y bundles. La primera contiene las evaluación de los individuos, la segunda los atributos y niveles utilizados en el estudio, y la última, bundles, contiene la descripción de los perfiles de producto.
# -- Read in conjoint rating data
tiresBundles <- readWorksheet(tireData, rownames=1, sheet = "bundles", header = TRUE) #load the set of bundles rated by informants
tiresDesign <- readWorksheet(tireData, sheet = "design", header = TRUE) #read conjoint desing
tiresDesign.l <- df2list(tiresDesign)
tiresDesign.l## $X.Brand
## [1] "Goodstone" "Pirogi" "Machismo"
##
## $Price
## [1] "$69,99" "$74,99" "$79,99"
##
## $Life
## [1] "50000km" "60000km" "70000km"
##
## $Hazard
## [1] "Yes" "No"
tiresRatings <- readWorksheet(tireData, rownames=1, sheet = "ratings", header = TRUE)También podemos leer los datos en formato csv.
tirebundles <- read.table("tirebundles.csv", row.names=1, header=T, dec = ".", sep=";")
tiredesign <- read.table("tiredesign.csv", header=T, sep=";")
tiresDesign.l <- df2list(tiresDesign)
tiresDesign.l## $X.Brand
## [1] "Goodstone" "Pirogi" "Machismo"
##
## $Price
## [1] "$69,99" "$74,99" "$79,99"
##
## $Life
## [1] "50000km" "60000km" "70000km"
##
## $Hazard
## [1] "Yes" "No"
tireratings <- read.table("tireratings.csv", row.names=1, header=T, sep=";")Un vez leidos los datos podemos comprobar si la lectura de datos ha sido correcta. Para ello utilizamos las funciones head(), tail()y dim().
tiredesign #conjoint design## Brand Price Life Hazard
## 1 Goodstone $69,99 50000km Yes
## 2 Pirogi $74,99 60000km No
## 3 Machismo $79,99 70000km
dim(tirebundles) #check the dimensions of bundles data frame## [1] 18 4
class(tirebundles) #ckeck the data class## [1] "data.frame"
head(tirebundles)## Brand Price Life Hazard
## bundle1 Machismo $74.99 50000km No
## bundle2 Goodstone $69.99 50000km No
## bundle3 Pirogi $74.99 70000km No
## bundle4 Goodstone $79.99 70000km Yes
## bundle5 Pirogi $74.99 70000km No
## bundle6 Machismo $69.99 70000km Yes
tail(tirebundles)## Brand Price Life Hazard
## bundle13 Goodstone $74.99 60000km Yes
## bundle14 Pirogi $69.99 60000km Yes
## bundle15 Goodstone $74.99 60000km Yes
## bundle16 Machismo $79.99 60000km No
## bundle17 Machismo $79.99 60000km No
## bundle18 Machismo $69.99 70000km Yes
head(tireratings) #checking data## bundle1 bundle2 bundle3 bundle4 bundle5 bundle6 bundle7 bundle8
## Subj1 3 6 6 4 7 9 3 3
## Subj2 5 8 3 4 4 8 1 5
## Subj3 1 2 7 7 9 9 3 1
## Subj4 3 3 5 8 6 9 1 4
## Subj5 3 6 6 3 7 9 2 3
## bundle9 bundle10 bundle11 bundle12 bundle13 bundle14 bundle15
## Subj1 8 2 3 6 6 9 5
## Subj2 7 1 4 9 6 6 6
## Subj3 6 2 6 4 5 7 5
## Subj4 5 1 9 3 5 6 7
## Subj5 8 2 4 6 6 8 5
## bundle16 bundle17 bundle18
## Subj1 2 2 8
## Subj2 2 2 8
## Subj3 4 4 8
## Subj4 4 4 9
## Subj5 2 2 9
tail(tireratings)## bundle1 bundle2 bundle3 bundle4 bundle5 bundle6 bundle7 bundle8
## Subj1 3 6 6 4 7 9 3 3
## Subj2 5 8 3 4 4 8 1 5
## Subj3 1 2 7 7 9 9 3 1
## Subj4 3 3 5 8 6 9 1 4
## Subj5 3 6 6 3 7 9 2 3
## bundle9 bundle10 bundle11 bundle12 bundle13 bundle14 bundle15
## Subj1 8 2 3 6 6 9 5
## Subj2 7 1 4 9 6 6 6
## Subj3 6 2 6 4 5 7 5
## Subj4 5 1 9 3 5 6 7
## Subj5 8 2 4 6 6 8 5
## bundle16 bundle17 bundle18
## Subj1 2 2 8
## Subj2 2 2 8
## Subj3 4 4 8
## Subj4 4 4 9
## Subj5 2 2 9
dim(tireratings)## [1] 5 18