Files
turismo_spain/notebooks/04_propuesta_app_turismo.ipynb
T
fn-registry agent ea833af54c chore: initial sync
2026-04-28 22:13:09 +02:00

444 lines
19 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Propuesta: App de Turismo Espana\n",
"\n",
"**\"Que hago hoy?\" — Asistente inteligente para turistas en Espana**\n",
"\n",
"---\n",
"\n",
"## El mercado\n",
"\n",
"Espana recibio **96.8 millones de turistas internacionales** en 2025 (record historico), que generaron **134.710 millones de euros** en gasto. Es el segundo destino mundial y el mercado crece al +3.2% anual.\n",
"\n",
"Pero hay un problema: **nadie esta sirviendo bien a estos turistas con tecnologia**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## El hueco de mercado\n",
"\n",
"La competencia se divide en dos bloques que no se hablan entre si:\n",
"\n",
"### Apps genericas globales\n",
"**Wanderlog, TripIt, Tripsy** — organizan bien pero no saben nada de Espana. No te dicen que la Playa de las Catedrales necesita reserva previa, ni que en agosto el interior de Andalucia supera los 45°C.\n",
"\n",
"### Apps locales fragmentadas \n",
"**Espana Turismo** (~2.000 POIs), **Komoot** (solo rutas), **Infomedusa** (solo medusas en 107 playas) — apps de un solo proposito que no gestionan el viaje.\n",
"\n",
"**Nadie esta haciendo una app nativa, rapida, con offline real, centrada exclusivamente en Espana, que combine asistencia durante el viaje con descubrimiento local autentico.**"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Setup OK\n"
]
}
],
"source": [
"import pandas as pd\n",
"import matplotlib\n",
"matplotlib.use('Agg')\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.patches as mpatches\n",
"import numpy as np\n",
"from pathlib import Path\n",
"import os\n",
"import warnings\n",
"warnings.filterwarnings('ignore')\n",
"\n",
"plt.rcParams.update({\n",
" 'figure.figsize': (10, 5), 'figure.dpi': 150, 'font.size': 11,\n",
" 'axes.titlesize': 14, 'axes.titleweight': 'bold', 'axes.grid': True,\n",
" 'grid.alpha': 0.3, 'figure.facecolor': 'white',\n",
"})\n",
"\n",
"FN_ROOT = Path(os.environ.get('FN_REGISTRY_ROOT', '')).resolve() if os.environ.get('FN_REGISTRY_ROOT') else Path.home() / 'fn_registry'\n",
"VAULT = FN_ROOT / 'vaults' / 'turismo_spain'\n",
"RAW = VAULT / 'raw'\n",
"FUENTES = RAW / 'turismo-espana-2025' / 'fuentes'\n",
"print('Setup OK')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pilar 1: Asistente diario contextual\n",
"\n",
"> *\"Hoy hace 38°C en Sevilla, evita pasear hasta las 19h. A 25 min tienes Italica con sombra y poca gente. O baja a la playa de Bolonia (1h 45min), bandera verde hoy.\"*\n",
"\n",
"El asistente combina GPS + clima + hora + preferencias del usuario para generar planes personalizados cada manana.\n",
"\n",
"### Por que funciona — datos que lo respaldan\n",
"\n",
"El **97% de los turistas se declaran satisfechos** con Espana, y el **69% tiene intencion de volver**. Pero la motivacion esta cambiando: los motivos culturales crecieron +32% y los gastronomicos +28% desde 2019. El turista ya no quiere un listado de TripAdvisor — quiere contexto."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(10, 4.5))\n",
"\n",
"motivos = {\n",
" 'Clima': 26.0,\n",
" 'Cultura': 15.7,\n",
" 'Familiares/amigos': 12.6,\n",
" 'Precio vuelos': 56.0,\n",
" 'Precio alojamiento': 55.0,\n",
" 'Gastronomia': 31.0,\n",
"}\n",
"\n",
"cats = list(motivos.keys())\n",
"vals = list(motivos.values())\n",
"colors = ['#2563EB', '#7C3AED', '#9CA3AF', '#D97706', '#D97706', '#059669']\n",
"\n",
"bars = ax.barh(cats, vals, color=colors, edgecolor='white')\n",
"for bar, v in zip(bars, vals):\n",
" ax.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2, f'{v}%', va='center', fontweight='bold')\n",
"\n",
"ax.set_title('Motivaciones del turista para elegir Espana', pad=15)\n",
"ax.set_xlabel('% de turistas')\n",
"ax.set_xlim(0, 70)\n",
"ax.spines['top'].set_visible(False)\n",
"ax.spines['right'].set_visible(False)\n",
"\n",
"# Annotation\n",
"ax.annotate('Cultura +32% vs 2019\\nGastronomia +28% vs 2019',\n",
" xy=(40, 3.5), fontsize=10, color='#7C3AED',\n",
" bbox=dict(boxstyle='round,pad=0.5', facecolor='#F3E8FF', edgecolor='#7C3AED', alpha=0.8))\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pilar 2: Descubrimiento \"fuera del radar\"\n",
"\n",
"Espana tiene un problema de **sobreturismo concentrado**: Cataluna (20.1M turistas, +0.6%) y las islas estan saturadas. Pero al mismo tiempo, **ciudades y zonas espectaculares crecen a ritmos de +20-35%** — Algeciras, Badajoz, Murcia, Merida, Malaga.\n",
"\n",
"La app puede ser el **puente** entre la saturacion costera y el interior desconocido: pueblos, calas, rutas, mercados locales, fiestas populares, bodegas."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Contraste: destinos saturados vs emergentes\n",
"data = {\n",
" 'Destino': ['Barcelona', 'Palma', 'Lloret de Mar', 'Salou',\n",
" 'Algeciras', 'Badajoz', 'Murcia', 'Merida', 'Malaga', 'Pontevedra', 'A Coruna', 'Cangas de Onis'],\n",
" 'Crecimiento': [0.6, 2.6, 1.5, 1.0,\n",
" 34.6, 30.1, 24.0, 24.7, 17.1, 19.2, 18.5, 18.1],\n",
" 'Tipo': ['Saturado']*4 + ['Emergente']*8\n",
"}\n",
"df = pd.DataFrame(data).sort_values('Crecimiento', ascending=True)\n",
"\n",
"fig, ax = plt.subplots(figsize=(10, 6))\n",
"colors = ['#DC2626' if t == 'Saturado' else '#059669' for t in df['Tipo']]\n",
"ax.barh(df['Destino'], df['Crecimiento'], color=colors, edgecolor='white')\n",
"\n",
"for i, (_, row) in enumerate(df.iterrows()):\n",
" ax.text(row['Crecimiento'] + 0.5, i, f\"+{row['Crecimiento']}%\", va='center', fontweight='bold', fontsize=9)\n",
"\n",
"ax.set_title('Destinos saturados vs emergentes — Crecimiento turismo extranjero 2024→2025', pad=15)\n",
"ax.set_xlabel('Crecimiento interanual %')\n",
"ax.axvline(x=3.2, color='gray', linestyle='--', linewidth=0.8)\n",
"ax.text(3.5, 11.5, 'Media\\nnacional', fontsize=8, color='gray')\n",
"ax.spines['top'].set_visible(False)\n",
"ax.spines['right'].set_visible(False)\n",
"\n",
"ax.legend([mpatches.Patch(fc='#DC2626'), mpatches.Patch(fc='#059669')],\n",
" ['Destinos clasicos (saturados)', 'Destinos emergentes'], loc='lower right')\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pilar 3: Offline-first con datos pre-descargados\n",
"\n",
"El turista descarga la zona donde va a estar y tiene **mapas, POIs, horarios, telefonos de emergencia y frases** sin necesitar datos moviles.\n",
"\n",
"Esto es critico: **33.6 millones de turistas** (34.7%) se alojan fuera de hoteles — en alquiler vacacional, vivienda propia o con familiares. Muchos estan en **zonas rurales o costeras con mala cobertura**. Y el segmento crece: vivienda propia +8.5%, alquiler +5.3%."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"aloj = pd.read_csv(FUENTES / '07_FRONTUR_2025_alojamiento.csv')\n",
"a = aloj[aloj['tipo_alojamiento'].str.contains(' - ') & ~aloj['tipo_alojamiento'].str.contains('Total')].copy()\n",
"a['tipo'] = a['tipo_alojamiento'].str.split(' - ').str[-1]\n",
"\n",
"# Dividir en \"con recepcionista\" vs \"sin recepcionista\"\n",
"a['segmento'] = a['tipo'].apply(lambda x: 'Con asistencia (hotel)' if x == 'Hotelero' else 'Sin asistencia — necesita la app')\n",
"seg = a.groupby('segmento')['turistas_acum2025'].sum().sort_values(ascending=True)\n",
"\n",
"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5))\n",
"\n",
"# Pie segmento\n",
"ax1.pie(seg, labels=seg.index, autopct='%1.1f%%', startangle=90,\n",
" colors=['#DC2626', '#2563EB'], textprops={'fontsize': 10})\n",
"ax1.set_title('Turistas segun tipo de asistencia', pad=15)\n",
"\n",
"# Crecimiento por tipo\n",
"a_sort = a.sort_values('variacion_anual_pct', ascending=True)\n",
"c = ['#059669' if v > 3 else '#2563EB' for v in a_sort['variacion_anual_pct']]\n",
"ax2.barh(a_sort['tipo'], a_sort['variacion_anual_pct'], color=c, edgecolor='white')\n",
"for i, (_, row) in enumerate(a_sort.iterrows()):\n",
" ax2.text(row['variacion_anual_pct'] + 0.2, i, f\"+{row['variacion_anual_pct']}%\", va='center', fontsize=10, fontweight='bold')\n",
"ax2.set_title('Crecimiento por tipo de alojamiento', pad=10)\n",
"ax2.spines['top'].set_visible(False)\n",
"ax2.spines['right'].set_visible(False)\n",
"ax2.annotate('Segmentos que\\nmas necesitan la app', xy=(7, 3.5), fontsize=9, color='#059669',\n",
" bbox=dict(boxstyle='round', facecolor='#ECFDF5', edgecolor='#059669'))\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Publico objetivo: quien usa la app\n",
"\n",
"Los datos de FRONTUR/EGATUR definen el target con precision:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Cobertura del MVP con 5 idiomas:\n",
" Ingles (UK+USA+IE+Nordicos): 31.8M = 33%\n",
" Frances (FR+BE): 15.9M = 16%\n",
" Aleman (DE+CH): 14.3M = 15%\n",
" Italiano: 5.7M = 6%\n",
" Neerlandes (NL): 5.0M = 5%\n",
" TOTAL: 72.7M = 75% del mercado\n"
]
}
],
"source": [
"paises = pd.read_csv(FUENTES / '01_FRONTUR_2025_anual_paises.csv')\n",
"gasto = pd.read_csv(FUENTES / '05_EGATUR_2025_gasto_paises.csv')\n",
"\n",
"# Merge paises con gasto\n",
"p = paises[paises['pais'] != 'TOTAL'].copy()\n",
"g = gasto[~gasto['pais'].isin(['TOTAL', 'Resto'])].copy()\n",
"merged = p.merge(g[['pais', 'gasto_medio_por_turista_eur_aprox']], on='pais', how='left').dropna()\n",
"\n",
"# Idioma por pais\n",
"idioma_map = {\n",
" 'Reino Unido': 'Ingles', 'Francia': 'Frances', 'Alemania': 'Aleman',\n",
" 'Italia': 'Italiano', 'Paises Bajos': 'Neerlandes', 'Belgica': 'Frances/Neerlandes',\n",
" 'Estados Unidos': 'Ingles', 'Irlanda': 'Ingles', 'Portugal': 'Portugues',\n",
" 'Suiza': 'Aleman/Frances', 'Paises Nordicos': 'Ingles (comun)',\n",
"}\n",
"merged['idioma'] = merged['pais'].map(idioma_map).fillna('Otro')\n",
"\n",
"fig, ax = plt.subplots(figsize=(10, 6))\n",
"\n",
"idioma_colors = {\n",
" 'Ingles': '#DC2626', 'Frances': '#2563EB', 'Aleman': '#D97706',\n",
" 'Italiano': '#059669', 'Neerlandes': '#7C3AED', 'Portugues': '#0891B2',\n",
" 'Frances/Neerlandes': '#4F46E5', 'Aleman/Frances': '#EA580C', 'Ingles (comun)': '#F87171',\n",
"}\n",
"c = [idioma_colors.get(i, '#9CA3AF') for i in merged['idioma']]\n",
"\n",
"scatter = ax.scatter(merged['turistas_millones'], merged['gasto_medio_por_turista_eur_aprox'],\n",
" s=merged['cuota_pct'] * 30, c=c, alpha=0.8, edgecolors='white', linewidth=1)\n",
"\n",
"for _, row in merged.iterrows():\n",
" ax.annotate(row['pais'], (row['turistas_millones'], row['gasto_medio_por_turista_eur_aprox']),\n",
" textcoords='offset points', xytext=(5, 5), fontsize=8)\n",
"\n",
"ax.set_title('Mercados emisores: volumen vs gasto por turista', pad=15)\n",
"ax.set_xlabel('Turistas (millones)')\n",
"ax.set_ylabel('Gasto medio por turista (EUR)')\n",
"ax.spines['top'].set_visible(False)\n",
"ax.spines['right'].set_visible(False)\n",
"\n",
"# Cuadrante alto valor\n",
"ax.axhline(y=1000, color='gray', linestyle=':', alpha=0.5)\n",
"ax.axvline(x=5, color='gray', linestyle=':', alpha=0.5)\n",
"ax.text(12, 1400, 'ALTO VOLUMEN\\n+ ALTO GASTO', fontsize=9, color='#DC2626', fontweight='bold',\n",
" bbox=dict(boxstyle='round', facecolor='#FEF2F2', edgecolor='#DC2626', alpha=0.8))\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"# Calcular cobertura por idioma\n",
"en = p[p['pais'].isin(['Reino Unido', 'Estados Unidos', 'Irlanda', 'Paises Nordicos'])]['turistas_millones'].sum()\n",
"fr = p[p['pais'].isin(['Francia', 'Belgica'])]['turistas_millones'].sum()\n",
"de = p[p['pais'].isin(['Alemania', 'Suiza'])]['turistas_millones'].sum()\n",
"it = p[p['pais'] == 'Italia']['turistas_millones'].sum()\n",
"nl = p[p['pais'] == 'Paises Bajos']['turistas_millones'].sum()\n",
"total = 96.8\n",
"\n",
"print(f'Cobertura del MVP con 5 idiomas:')\n",
"print(f' Ingles (UK+USA+IE+Nordicos): {en:.1f}M = {en/total*100:.0f}%')\n",
"print(f' Frances (FR+BE): {fr:.1f}M = {fr/total*100:.0f}%')\n",
"print(f' Aleman (DE+CH): {de:.1f}M = {de/total*100:.0f}%')\n",
"print(f' Italiano: {it:.1f}M = {it/total*100:.0f}%')\n",
"print(f' Neerlandes (NL): {nl:.1f}M = {nl/total*100:.0f}%')\n",
"print(f' TOTAL: {en+fr+de+it+nl:.1f}M = {(en+fr+de+it+nl)/total*100:.0f}% del mercado')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Diferenciadores vs competencia\n",
"\n",
"| Caracteristica | Wanderlog/TripIt | Espana Turismo | Komoot | **Esta app** |\n",
"|---------------|-----------------|----------------|--------|-------------|\n",
"| Datos curados de Espana | No | Parcial (POIs) | Solo rutas | **Si — horarios, mareas, restricciones, fiestas** |\n",
"| Asistente contextual (GPS+clima+hora) | No | No | No | **Si — plan diario personalizado** |\n",
"| Offline real | Parcial | No | Solo rutas | **Si — zona completa descargable** |\n",
"| Multiidioma (EN/FR/DE/IT/NL) | EN only | ES only | Multi | **Si — 5 idiomas = 62% del mercado** |\n",
"| Nativa (Kotlin/Swift) | React Native | React Native | Nativa | **Nativa — experiencia fluida** |\n",
"| Descubrimiento local | No | Basico | No | **Si — pueblos, mercados, fiestas, chiringuitos** |\n",
"| Gestion del viaje | Si | No | No | **Fase 2** |"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Modelo de negocio\n",
"\n",
"**App gratuita para el turista** (maxima adopcion en un mercado de 96.8M usuarios potenciales).\n",
"\n",
"Tres fuentes de ingreso:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"gasto_ccaa = pd.read_csv(FUENTES / '16_gasto_ccaa_acumulado.csv')\n",
"\n",
"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5))\n",
"\n",
"# Modelo de negocio visual\n",
"canales = ['Negocios locales\\n(recomendacion destacada)', 'Oficinas turismo CCAA\\n(desestacionalizacion)', 'Premium\\n(funciones avanzadas)']\n",
"potencial = [70, 20, 10] # % del revenue mix estimado\n",
"colors = ['#2563EB', '#059669', '#D97706']\n",
"ax1.pie(potencial, labels=canales, autopct='%d%%', colors=colors, startangle=90, textprops={'fontsize': 9})\n",
"ax1.set_title('Revenue mix estimado', pad=15)\n",
"\n",
"# Gasto por CCAA\n",
"gc = gasto_ccaa.sort_values('cuota_gasto_pct', ascending=True)\n",
"ax2.barh(gc['ccaa'], gc['cuota_gasto_pct'], color='#2563EB', edgecolor='white')\n",
"for i, (_, row) in enumerate(gc.iterrows()):\n",
" ax2.text(row['cuota_gasto_pct'] + 0.3, i, f\"{row['cuota_gasto_pct']}%\", va='center', fontsize=10)\n",
"ax2.set_title('Cuota de gasto turistico por CCAA', pad=10)\n",
"ax2.set_xlabel('% del gasto total')\n",
"ax2.spines['top'].set_visible(False)\n",
"ax2.spines['right'].set_visible(False)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"print('Potencial B2G: las 7 principales CCAA tienen presupuestos de turismo de 8-12 cifras.')\n",
"print('Todas buscan activamente herramientas para desestacionalizar y dispersar turistas.')\n",
"print(f'Solo el gasto de Andalucia (15.9%) supone ~21.400M EUR — el mercado es enorme.')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Roadmap MVP\n",
"\n",
"### Fase 1 — MVP (3-4 meses)\n",
"- Asistente diario contextual (GPS + clima + hora)\n",
"- Base de datos curada: 500 POIs en 3 regiones piloto (Andalucia, Cataluna, Baleares)\n",
"- Offline-first: descarga por region\n",
"- Idiomas: ingles + espanol\n",
"- Nativo iOS (Swift) + Android (Kotlin)\n",
"\n",
"### Fase 2 — Expansion (3 meses)\n",
"- 5.000+ POIs en todas las CCAA\n",
"- Frances, aleman, italiano, neerlandes\n",
"- Monetizacion: negocios locales destacados\n",
"- Integracion con oficinas de turismo regionales\n",
"\n",
"### Fase 3 — Plataforma\n",
"- Gestion del viaje (itinerarios, reservas)\n",
"- Contenido generado por locales (validado)\n",
"- API para terceros (hoteles, tour operators)\n",
"\n",
"---\n",
"\n",
"## Conclusion\n",
"\n",
"Los datos son inequivocos:\n",
"\n",
"- **96.8M turistas** que necesitan mejor tecnologia\n",
"- **33.6M fuera de hoteles** sin asistencia\n",
"- **Cultura (+32%) y gastronomia (+28%)** como motivaciones en alza\n",
"- **Destinos emergentes creciendo al +20-35%** mientras los clasicos se estancan\n",
"- **5 idiomas cubren el 62%** del mercado\n",
"- **Cero competidores** en el nicho de app nativa + offline + asistencia contextual + Espana\n",
"\n",
"El hueco existe, el mercado es masivo, y los datos lo respaldan.\n",
"\n",
"---\n",
"\n",
"*Todos los datos provienen de fuentes oficiales: INE (FRONTUR/EGATUR), Ministerio de Industria y Turismo, Turespaña, Skyscanner/Dataestur.*\n",
"\n",
"*Abril 2026*"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}