Santiago Quiñones Cuenca
Software Developer and Educator, Master in Software Engineering, Research UTPL {Loja, Ecuador} Repositories: http://github.com/lsantiago
Programación funcional y reactiva - Computación
Datos
Datos
Definición
Los "datos" son representaciones simbólicas de hechos, conceptos o instrucciones que se recopilan, procesan y analizan para extraer información valiosa. Pueden ser cualitativos o cuantitativos, y su importancia radica en su capacidad para respaldar decisiones, generar conocimiento y facilitar innovaciones en diversos campos.
Importancia de los datos
Debate en grupos
Grupos A: Beneficios de los datos
Grupos B: Desafios y preocupaciones
Análisis exploratorio de datos (EDA)
Análisis exploratorio de datos (EDA)
Análisis exploratorio de datos (EDA)
Uso de análisis exploratorio
Análisis exploratorio de datos (EDA)
Un ejemplo integrado sería: Imagina que analizas datos de una aplicación de fitness:
Estadísticas descriptivas
La moda: el valor más frecuente que toma una variable
Estadísticas descriptivas
La moda: el valor más frecuente que toma una variable
import kantan.csv._
import kantan.csv.ops._
import kantan.csv.generic._
import java.io.File
case class Goleador(
jugador: String,
club: String,
nacionalidad: String,
goles: Int,
autogol: String
)
object Ejemplo3_Goleadores_Moda extends App {
// Leer el archivo CSV
val path2DataFile2 = "data/Goleadores_LigaPro_2019.csv"
val dataSource2 = new File(path2DataFile2)
.readCsv[List, Goleador](rfc.withHeader.withCellSeparator(';'))
// Obtener datos válidos
val rows_ok = dataSource2.collect {
case Right(goleador) => goleador
}
// Calcular la moda usando programación funcional
val moda = rows_ok
.map(_.goles) // Obtener solo los goles
.groupBy(identity) // Agrupar valores idénticos
.map { case (gol, lista) => // Transformar a (gol, frecuencia)
(gol, lista.length)
}
.maxBy(_._2) // Obtener el de mayor frecuencia
// Mostrar resultados
println(s"La moda es: ${moda._1} goles (aparece ${moda._2} veces)")
// Mostrar jugadores que tienen esa cantidad de goles
println("\nJugadores con esta cantidad de goles:")
rows_ok
.filter(_.goles == moda._1)
.foreach(j => println(s"${j.jugador} (${j.club})"))
}
// Si tenemos estos goles: [2, 2, 3, 2, 3, 4]
Map(
2 -> List(2, 2, 2), // tres jugadores con 2 goles
3 -> List(3, 3), // dos jugadores con 3 goles
4 -> List(4) // un jugador con 4 goles
)
Estadísticas descriptivas
La media: "el promedio"
Estadísticas descriptivas
La media: "el promedio"
package b2s12.jugadores
import kantan.csv._
import kantan.csv.ops._
import kantan.csv.generic._
import java.io.File
import models.Goleador
object EDA2_promedio extends App {
// Leer el archivo CSV
val path2DataFile2 = "data/Goleadores_LigaPro_2019.csv"
val dataSource2 = new File(path2DataFile2)
.readCsv[List, Goleador](rfc.withHeader.withCellSeparator(';'))
// Obtener datos válidos
val rows_ok = dataSource2.collect {
case Right(goleador) => goleador
}
// Cálculo del promedio usando métodos de colección
val promedio = rows_ok.map(_.goles).sum.toDouble / rows_ok.length
// Mostrar resultados
println(s"El promedio de goles es: $promedio")
// Estadísticas adicionales
val totalJugadores = rows_ok.length
val jugadoresArribaPromedio = rows_ok.count(_.goles > promedio)
val jugadoresAbajoPromedio = rows_ok.count(_.goles < promedio)
println("\nEstadísticas:")
println(s"Total de jugadores: $totalJugadores")
println(s"Jugadores arriba del promedio: $jugadoresArribaPromedio")
println(s"Jugadores abajo del promedio: $jugadoresAbajoPromedio")
}
Estadísticas descriptivas
El rango: el "tamaño" de los valores, la diferencia entre el valor más grande y el más pequeño
Estadísticas descriptivas
El rango: el "tamaño" de los valores, la diferencia entre el valor más grande y el más pequeño
// Calcular el rango usando programación funcional
val goles = rows_ok.map(_.goles)
val maximoGoles = goles.max
val minimoGoles = goles.min
val rango = maximoGoles - minimoGoles
// Mostrar resultados
println(s"Rango de goles: $rango")
println(s"Valor máximo: $maximoGoles goles")
println(s"Valor mínimo: $minimoGoles goles")
Estadísticas descriptivas
Desviación estándar: cuantifica la dispersión del conjunto de datos numéricos
Estadísticas descriptivas
Desviación estándar: cuantifica la dispersión del conjunto de datos numéricos
// Calcular promedio (necesario para la desviación)
val promedio = goles.sum.toDouble / goles.length
// Calcular desviación estándar
val desviacionEstandar = sqrt(
goles.map(gol => math.pow(gol - promedio, 2)) // Diferencia al cuadrado
.sum / goles.length // Promedio de las diferencias
)
Estadísticas descriptivas
Correlación: Ayuda a entender entre las dos variables numéricas. Mide la fuerza y dirección entre dos variables numéricas
El valor va de -1 a +1:
Estadísticas descriptivas
Correlación: Ayuda a entender entre las dos variables numéricas. Mide la fuerza y dirección entre dos variables numéricas
package b2s12.jugadores
import kantan.csv._
import kantan.csv.ops._
import kantan.csv.generic._
import java.io.File
import scala.math.{sqrt, pow}
// Case class extendido con partidos jugados
case class GoleadorExtendido(
jugador: String,
club: String,
nacionalidad: String,
goles: Int,
autogol: String,
partidos: Int // Nuevo campo
)
object EDA5_correlacion extends App{
// Función para calcular correlación de Pearson
def calcularCorrelacion(x: List[Double], y: List[Double]): Double = {
require(x.length == y.length && x.nonEmpty, "Las listas deben tener el mismo tamaño y no estar vacías")
val n = x.length
val promedioX = x.sum / n
val promedioY = y.sum / n
val numerador = x.zip(y).map { case (xi, yi) =>
(xi - promedioX) * (yi - promedioY)
}.sum
val denominadorX = sqrt(x.map(xi => pow(xi - promedioX, 2)).sum)
val denominadorY = sqrt(y.map(yi => pow(yi - promedioY, 2)).sum)
numerador / (denominadorX * denominadorY)
}
// Simulamos algunos datos de ejemplo (en un caso real, estos vendrían del CSV)
val datos = List(
GoleadorExtendido("Jugador1", "Club A", "ECU", 10, "", 20),
GoleadorExtendido("Jugador2", "Club B", "ARG", 15, "", 25),
GoleadorExtendido("Jugador3", "Club C", "ECU", 8, "", 18),
GoleadorExtendido("Jugador4", "Club D", "COL", 20, "", 30),
GoleadorExtendido("Jugador5", "Club E", "ECU", 12, "", 22)
)
// Extraer listas de goles y partidos
val goles = datos.map(_.goles.toDouble)
val partidos = datos.map(_.partidos.toDouble)
// Calcular correlación
val correlacion = calcularCorrelacion(goles, partidos)
// Mostrar resultados
println(f"Correlación entre goles y partidos jugados: $correlacion%.4f")
// Interpretación de la correlación
val interpretacion = correlacion match {
case r if r > 0.7 => "Correlación positiva fuerte"
case r if r > 0.3 => "Correlación positiva moderada"
case r if r > -0.3 => "Correlación débil o nula"
case r if r > -0.7 => "Correlación negativa moderada"
case _ => "Correlación negativa fuerte"
}
println(s"Interpretación: $interpretacion")
// Mostrar datos para visualización
println("\nDatos:")
println("Jugador Goles Partidos Goles/Partido")
println("-" * 45)
datos.foreach { j =>
val golesPartido = j.goles.toDouble / j.partidos
println(f"${j.jugador}%-15s ${j.goles}%6d ${j.partidos}%9d ${golesPartido}%13.2f")
}
}
Limpieza de datos
Proyecto Integrador o bimestral
Comentarios
Dato estructurao
Date
Columna release_date
• Formato: yyyy-MM-dd
• No existe en todas las filas
import java.time.LocalDate
import java.time.format.DateTimeFormatter
// Procesar las fechas de manera segura
val releaseDateList = validRows
.flatMap(_.release_date) // extrae los valores de los Some y elimina los None
.filter(_.nonEmpty) // Elimina strings vacíos
.map { dateStr =>
try {
Some(LocalDate.parse(dateStr, dateFormatter))
} catch {
case _: Exception => None
}
}
.flatten // Elimina los None resultantes de fechas inválidas
JavaScript Object Notation (JSON)
Aproximación
JavaScript Object Notation (JSON)
Objetos
JSON
Arreglos
JSON
Herramientas
JSONViewer: http://jsonviewer.stack.hu/
Herramientas
{
"nombre": "Cristina Guaman",
"edad": 30,
"esProfesor": true,
"direccion": {
"ciudad": "Loja",
"pais": "Ecuador"
},
"hobbies": ["leer", "cocinar", "viajar"],
"certificados": null
}
JSON
Movies DataSet
JSON
Movies DataSet
JSON
Scala
• Existen varias librerías para trabajar con JSON y Scala
• Una de ellas se denomina Play-JSON
• Información: https://www.playframework.com/documentation/2.8.x/ ScalaJson
JSON
Ejemplo base
import play.api.libs.json._
// Case class simple para representar una persona
case class Persona(nombre: String, edad: Int, email: Option[String])
object json_simple extends App {
// JSON de ejemplo como String
val jsonString = """
{
"nombre": "Ana García",
"edad": 28,
"email": "ana@email.com"
}
"""
// Formato implícito para convertir entre JSON y el case class
// Le dice a Play JSON cómo convertir entre JSON y el case class Persona
implicit val formatoPersona = Json.format[Persona]
// Parseamos el JSON string a un objeto Persona
val resultado = Json.parse(jsonString).as[Persona]
// Mostramos el resultado
println(s"Nombre: ${resultado.nombre}")
println(s"Edad: ${resultado.edad}")
println(s"Email: ${resultado.email.getOrElse("No disponible")}")
}
JSON
JSON con dos registros
package b2s12.limpieza
import play.api.libs.json._
case class Persona2(nombre: String, edad: Int, email: Option[String])
object json_dos_personas extends App{
// JSON de ejemplo como String con array de personas
val jsonString = """
{
"personas": [
{
"nombre": "Ana García",
"edad": 28,
"email": "ana@email.com"
},
{
"nombre": "Carlos López",
"edad": 35,
"email": null
}
]
}
"""
implicit val formatoPersona = Json.format[Persona2]
// Parseamos el JSON string
// Convierte el string JSON en un objeto JSON que podemos manipular
val json = Json.parse(jsonString)
// Obtenemos el array de personas (navega al campo "personas" del JSON)
val personas = (json \ "personas").as[List[Persona2]]
// Mostramos los resultados
personas.foreach { persona =>
println(s"\nDatos de persona:")
println(s"Nombre: ${persona.nombre}")
println(s"Edad: ${persona.edad}")
println(s"Email: ${persona.email.getOrElse("No disponible")}")
}
}
JSON
JSON con problemas
package b2s12.limpieza
import play.api.libs.json._
case class Crew(
name: String,
department: String,
job: String,
profile_path: Option[String]
)
object json_mal_formado extends App {
// JSON mal estructurado como String
val jsonSucio = """
{
'crews': [
{
'name': 'John Smith',
'department': 'Directing',
'job': 'Director',
'profile_path': None
},
{
'name': 'Mary Johnson\\',
'department': 'Production',
'job': 'Producer',
'profile_path': '\\path\\to\\profile'
},
{
'name': 'Peter Parker',
'department': 'Camera',
'job': 'Cinematographer',
'profile_path': None,
'active': True
}
]
}
"""
// Función para limpiar el JSON
def cleanCrewJson(crewJson: String): String = {
crewJson
.trim
.replaceAll("'", "\"") // Reemplazar comillas simples por dobles
.replaceAll("None", "null") // Reemplazar None por null
.replaceAll("True", "true") // Normalizar booleanos
.replaceAll("False", "false")
.replaceAll("""\\""", "") // Eliminar escapes innecesarios
}
implicit val crewFormat = Json.format[Crew]
// Limpiamos y parseamos el JSON
val jsonLimpio = cleanCrewJson(jsonSucio)
val json = Json.parse(jsonLimpio)
// Extraemos la lista de crews
val crews = (json \ "crews").as[List[Crew]]
// Mostramos los resultados
println("Crews procesados:")
crews.foreach { crew =>
println("\nMiembro del equipo:")
println(s"Nombre: ${crew.name}")
println(s"Departamento: ${crew.department}")
println(s"Trabajo: ${crew.job}")
println(s"Perfil: ${crew.profile_path.getOrElse("No disponible")}")
}
// Mostramos el JSON limpio
println("\nJSON limpio generado:")
println(Json.prettyPrint(Json.toJson(Map("crews" -> crews))))
}
By Santiago Quiñones Cuenca
Introducción al Análisis exploratorio de datos (EDA) a través de estadísticas básicas.
Software Developer and Educator, Master in Software Engineering, Research UTPL {Loja, Ecuador} Repositories: http://github.com/lsantiago