From 0fbd2ab49799434e2b5369815d03b091d21c84bc Mon Sep 17 00:00:00 2001 From: Rikkonen Date: Fri, 8 Nov 2024 13:10:12 -0600 Subject: [PATCH 1/4] =?UTF-8?q?Versi=C3=B3n=202.0=20de=20mi=20tarea?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En esta versión le doy formato a los números lo que la hace más lenta pero más fácil de leer. --- .../DividirContarPromedio_MarthaRico_V2.0.go | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go diff --git a/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go b/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go new file mode 100644 index 00000000..123eacdd --- /dev/null +++ b/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go @@ -0,0 +1,270 @@ +/* Autor Martha Rico Diener +Equipo Los cantantes +Este programa procesa archivos CSV que contienen calificaciones de películas y +estadísticas sobre géneros. Utiliza concurrencia para manejar múltiples archivos +de calificaciones y calcula el promedio de calificaciones por género. +*/ + +package main + +/*Se importan varios paquetes necesarios para la funcionalidad del programa: +encoding/csv para manejar archivos CSV +fmt para imprimir en consola +log para registrar errores +os para operaciones de archivos +sort para ordenar datos, +strings para manipulación de cadenas +sync para gestionar concurrencia +time para medir el tiempo de ejecución. +*/ + +import ( + "encoding/csv" + "fmt" + "log" + "os" + "sort" + "strings" + "sync" + "time" + "strconv" +) + +func dividir() { //Esta función divide el archivo ratings en 10 partes iguales + + startTime := time.Now() //Para medir el tiempo de ejecución + + // Cargar el archivo CSV + df, headers, err := loadCSV("ratings.csv") + if err != nil { + panic(err) + } + + // Definir el número de partes en las que quieres dividir el archivo + N := 10 + + // Calcular el tamaño de cada parte + numRows := len(df) + rowsPerPart := numRows / N + + // Crear un loop para dividir el DataFrame y guardar cada parte + for i := 0; i < N; i++ { + startRow := i * rowsPerPart + endRow := startRow + rowsPerPart + + // Asegurarse de no exceder el número de filas + if i == N-1 { + endRow = numRows + } + + // Crear y guardar la parte como un nuevo archivo CSV + partDF := df[startRow:endRow] + err := saveCSV(fmt.Sprintf("ratings_part_%d.csv", i+1), headers, partDF) + if err != nil { + panic(err) + } + } + + fmt.Printf("El archivo ha sido dividido en %d partes.\n", N) + elapsedTime := time.Since(startTime) + fmt.Printf("Tiempo transcurrido hasta este moemnto: %s\n", elapsedTime) +} + +// loadCSV carga un archivo CSV y devuelve los datos como una matriz de cadenas y los encabezados +func loadCSV(filename string) ([][]string, []string, error) { + file, err := os.Open(filename) + if err != nil { + return nil, nil, err + } + defer file.Close() + + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + return nil, nil, err + } + + // Separar los encabezados de los datos + headers := records[0] + data := records[1:] + + return data, headers, nil +} + +// saveCSV guarda una parte de datos en un nuevo archivo CSV con encabezados +func saveCSV(filename string, headers []string, data [][]string) error { + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + + writer := csv.NewWriter(file) + defer writer.Flush() + + // Escribir los encabezados en el archivo CSV + if err := writer.Write(headers); err != nil { + return err + } + + // Escribir las filas en el archivo CSV + return writer.WriteAll(data) +} + +/* + **Función `processRatings`**: + - Toma el nombre de un archivo de calificaciones, + un mapa para almacenar estadísticas de géneros, + un mutex para sincronización y un `WaitGroup` + para esperar a que se completen las goroutines. +*/ + +func processRatings(ratingsFile string, genreData map[string]*GenreStats, mu *sync.Mutex, wg *sync.WaitGroup) { + defer wg.Done() + + //Abre el archivo CSV + file, err := os.Open(ratingsFile) + if err != nil { + log.Printf("Error al abrir %s: %v", ratingsFile, err) + return + } + defer file.Close() + + reader := csv.NewReader(file) + ratingsData, err := reader.ReadAll() + if err != nil { + log.Printf("Error al leer %s: %v", ratingsFile, err) + return + } + + for _, rating := range ratingsData[1:] { // Ignorar encabezados + movieId := rating[1] + var ratingValue float64 + if len(rating) > 2 { + _, err := fmt.Sscanf(rating[2], "%f", &ratingValue) // Leer la calificación como float + if err != nil { + log.Printf("Error al leer la calificación: %v", err) + } + } + + // Buscar el título y género correspondiente en moviesData + for _, movie := range moviesData[1:] { // Ignorar encabezados + if movie[0] == movieId { + genres := strings.Split(movie[2], "|") + mu.Lock() + for _, genre := range genres { + if _, exists := genreData[genre]; !exists { + genreData[genre] = &GenreStats{} + } + genreData[genre].Count++ + genreData[genre].Sum += ratingValue + } + mu.Unlock() + break + } + } + } +} + +var moviesData [][]string + +// Estructura para almacenar estadísticas de cada género +type GenreStats struct { + Count int + Sum float64 +} + + +// Función para formatear números con comas +func formatNumber(n int) string { + s := strconv.Itoa(n) + nLen := len(s) + if nLen <= 3 { + return s + } + + var result strings.Builder + for i, digit := range s { + if (nLen-i)%3 == 0 && i != 0 { + result.WriteRune(',') + } + result.WriteRune(digit) + } + return result.String() +} + +func main() { + + startTime := time.Now() //Para medir el tiempo de ejecución + + dividir() //dividir el archivo ratings.csv en 10 + + // Abrir y leer el archivo movies.csv + moviesFile, err := os.Open("movies.csv") + if err != nil { + log.Fatalf("Error al abrir movies.csv: %v", err) + } + defer moviesFile.Close() + + moviesReader := csv.NewReader(moviesFile) + moviesData, err = moviesReader.ReadAll() + if err != nil { + log.Fatalf("Error al leer movies.csv: %v", err) + } + + // Mapa para almacenar estadísticas de calificaciones por género + genreData := make(map[string]*GenreStats) + var mu sync.Mutex + var wg sync.WaitGroup + + // Procesar cada archivo de ratings concurrentemente + for i := 1; i <= 10; i++ { + wg.Add(1) + go processRatings(fmt.Sprintf("ratings_part_%d.csv", i), genreData, &mu, &wg) + } + + wg.Wait() + + // Crear un slice para almacenar los géneros y sus estadísticas + type genreStat struct { + genre string + count int + average float64 + } + + var sortedGenres []genreStat + for genre, stats := range genreData { + average := 0.0 + if stats.Count > 0 { + average = float64(stats.Sum) / float64(stats.Count) + } + sortedGenres = append(sortedGenres, genreStat{genre, stats.Count, average}) + } + + // Ordenar el slice por el nombre del género + sort.Slice(sortedGenres, func(i, j int) bool { + return sortedGenres[i].genre < sortedGenres[j].genre + }) + +/* +// Imprimir los resultados +fmt.Println("Conteo de calificaciones por género:") +for _, gs := range sortedGenres { + fmt.Printf("%s: \t \t %s \t \t %.2f\n", gs.genre, formatNumber(gs.count), gs.average) +} +*/ + +// Imprimir los resultados +fmt.Println() +fmt.Println("Conteo de calificaciones por género:") +fmt.Printf("%-20s|%20s\t|\t%20s\n", "Género", "Conteo", "Promedio de calificaciones") +fmt.Println("----------------------------------------------------------") +for _, gs := range sortedGenres { + // Ajusta los anchos según tus necesidades + fmt.Printf("%-20s| %20s\t|\t%6.2f\n", gs.genre, formatNumber(gs.count), gs.average) +} + + // Calcular y mostrar el tiempo total de ejecución + elapsedTime := time.Since(startTime) + fmt.Printf("Tiempo total de ejecución: %s\n", elapsedTime) +} From 9fdd4be5634f2ff552b417885cae075efdca74d4 Mon Sep 17 00:00:00 2001 From: Rikkonen Date: Fri, 8 Nov 2024 13:12:14 -0600 Subject: [PATCH 2/4] =?UTF-8?q?Versi=C3=B3n=20final=20de=20mi=20tarea?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En esta versión hago cambios y le doy formato a los números. Se hace más fácil de leer pero un poco más lenta. --- .../DividirContarPromedio_MarthaRico_V2.0.go | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 go/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go diff --git a/go/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go b/go/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go new file mode 100644 index 00000000..123eacdd --- /dev/null +++ b/go/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go @@ -0,0 +1,270 @@ +/* Autor Martha Rico Diener +Equipo Los cantantes +Este programa procesa archivos CSV que contienen calificaciones de películas y +estadísticas sobre géneros. Utiliza concurrencia para manejar múltiples archivos +de calificaciones y calcula el promedio de calificaciones por género. +*/ + +package main + +/*Se importan varios paquetes necesarios para la funcionalidad del programa: +encoding/csv para manejar archivos CSV +fmt para imprimir en consola +log para registrar errores +os para operaciones de archivos +sort para ordenar datos, +strings para manipulación de cadenas +sync para gestionar concurrencia +time para medir el tiempo de ejecución. +*/ + +import ( + "encoding/csv" + "fmt" + "log" + "os" + "sort" + "strings" + "sync" + "time" + "strconv" +) + +func dividir() { //Esta función divide el archivo ratings en 10 partes iguales + + startTime := time.Now() //Para medir el tiempo de ejecución + + // Cargar el archivo CSV + df, headers, err := loadCSV("ratings.csv") + if err != nil { + panic(err) + } + + // Definir el número de partes en las que quieres dividir el archivo + N := 10 + + // Calcular el tamaño de cada parte + numRows := len(df) + rowsPerPart := numRows / N + + // Crear un loop para dividir el DataFrame y guardar cada parte + for i := 0; i < N; i++ { + startRow := i * rowsPerPart + endRow := startRow + rowsPerPart + + // Asegurarse de no exceder el número de filas + if i == N-1 { + endRow = numRows + } + + // Crear y guardar la parte como un nuevo archivo CSV + partDF := df[startRow:endRow] + err := saveCSV(fmt.Sprintf("ratings_part_%d.csv", i+1), headers, partDF) + if err != nil { + panic(err) + } + } + + fmt.Printf("El archivo ha sido dividido en %d partes.\n", N) + elapsedTime := time.Since(startTime) + fmt.Printf("Tiempo transcurrido hasta este moemnto: %s\n", elapsedTime) +} + +// loadCSV carga un archivo CSV y devuelve los datos como una matriz de cadenas y los encabezados +func loadCSV(filename string) ([][]string, []string, error) { + file, err := os.Open(filename) + if err != nil { + return nil, nil, err + } + defer file.Close() + + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + return nil, nil, err + } + + // Separar los encabezados de los datos + headers := records[0] + data := records[1:] + + return data, headers, nil +} + +// saveCSV guarda una parte de datos en un nuevo archivo CSV con encabezados +func saveCSV(filename string, headers []string, data [][]string) error { + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + + writer := csv.NewWriter(file) + defer writer.Flush() + + // Escribir los encabezados en el archivo CSV + if err := writer.Write(headers); err != nil { + return err + } + + // Escribir las filas en el archivo CSV + return writer.WriteAll(data) +} + +/* + **Función `processRatings`**: + - Toma el nombre de un archivo de calificaciones, + un mapa para almacenar estadísticas de géneros, + un mutex para sincronización y un `WaitGroup` + para esperar a que se completen las goroutines. +*/ + +func processRatings(ratingsFile string, genreData map[string]*GenreStats, mu *sync.Mutex, wg *sync.WaitGroup) { + defer wg.Done() + + //Abre el archivo CSV + file, err := os.Open(ratingsFile) + if err != nil { + log.Printf("Error al abrir %s: %v", ratingsFile, err) + return + } + defer file.Close() + + reader := csv.NewReader(file) + ratingsData, err := reader.ReadAll() + if err != nil { + log.Printf("Error al leer %s: %v", ratingsFile, err) + return + } + + for _, rating := range ratingsData[1:] { // Ignorar encabezados + movieId := rating[1] + var ratingValue float64 + if len(rating) > 2 { + _, err := fmt.Sscanf(rating[2], "%f", &ratingValue) // Leer la calificación como float + if err != nil { + log.Printf("Error al leer la calificación: %v", err) + } + } + + // Buscar el título y género correspondiente en moviesData + for _, movie := range moviesData[1:] { // Ignorar encabezados + if movie[0] == movieId { + genres := strings.Split(movie[2], "|") + mu.Lock() + for _, genre := range genres { + if _, exists := genreData[genre]; !exists { + genreData[genre] = &GenreStats{} + } + genreData[genre].Count++ + genreData[genre].Sum += ratingValue + } + mu.Unlock() + break + } + } + } +} + +var moviesData [][]string + +// Estructura para almacenar estadísticas de cada género +type GenreStats struct { + Count int + Sum float64 +} + + +// Función para formatear números con comas +func formatNumber(n int) string { + s := strconv.Itoa(n) + nLen := len(s) + if nLen <= 3 { + return s + } + + var result strings.Builder + for i, digit := range s { + if (nLen-i)%3 == 0 && i != 0 { + result.WriteRune(',') + } + result.WriteRune(digit) + } + return result.String() +} + +func main() { + + startTime := time.Now() //Para medir el tiempo de ejecución + + dividir() //dividir el archivo ratings.csv en 10 + + // Abrir y leer el archivo movies.csv + moviesFile, err := os.Open("movies.csv") + if err != nil { + log.Fatalf("Error al abrir movies.csv: %v", err) + } + defer moviesFile.Close() + + moviesReader := csv.NewReader(moviesFile) + moviesData, err = moviesReader.ReadAll() + if err != nil { + log.Fatalf("Error al leer movies.csv: %v", err) + } + + // Mapa para almacenar estadísticas de calificaciones por género + genreData := make(map[string]*GenreStats) + var mu sync.Mutex + var wg sync.WaitGroup + + // Procesar cada archivo de ratings concurrentemente + for i := 1; i <= 10; i++ { + wg.Add(1) + go processRatings(fmt.Sprintf("ratings_part_%d.csv", i), genreData, &mu, &wg) + } + + wg.Wait() + + // Crear un slice para almacenar los géneros y sus estadísticas + type genreStat struct { + genre string + count int + average float64 + } + + var sortedGenres []genreStat + for genre, stats := range genreData { + average := 0.0 + if stats.Count > 0 { + average = float64(stats.Sum) / float64(stats.Count) + } + sortedGenres = append(sortedGenres, genreStat{genre, stats.Count, average}) + } + + // Ordenar el slice por el nombre del género + sort.Slice(sortedGenres, func(i, j int) bool { + return sortedGenres[i].genre < sortedGenres[j].genre + }) + +/* +// Imprimir los resultados +fmt.Println("Conteo de calificaciones por género:") +for _, gs := range sortedGenres { + fmt.Printf("%s: \t \t %s \t \t %.2f\n", gs.genre, formatNumber(gs.count), gs.average) +} +*/ + +// Imprimir los resultados +fmt.Println() +fmt.Println("Conteo de calificaciones por género:") +fmt.Printf("%-20s|%20s\t|\t%20s\n", "Género", "Conteo", "Promedio de calificaciones") +fmt.Println("----------------------------------------------------------") +for _, gs := range sortedGenres { + // Ajusta los anchos según tus necesidades + fmt.Printf("%-20s| %20s\t|\t%6.2f\n", gs.genre, formatNumber(gs.count), gs.average) +} + + // Calcular y mostrar el tiempo total de ejecución + elapsedTime := time.Since(startTime) + fmt.Printf("Tiempo total de ejecución: %s\n", elapsedTime) +} From 85d49b0dd1fb8ff6ff2837b7786b83bc11a17782 Mon Sep 17 00:00:00 2001 From: Rikkonen Date: Fri, 8 Nov 2024 13:14:58 -0600 Subject: [PATCH 3/4] =?UTF-8?q?Versi=C3=B3n=20final?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Esta es la versión final de mi código. Le doy formato a los número lo que hace que se ejecute más lento. --- .../DividirContarPromedio_Martha_Rico_V2.jl | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_Martha_Rico_V2.jl diff --git a/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_Martha_Rico_V2.jl b/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_Martha_Rico_V2.jl new file mode 100644 index 00000000..8fb64c67 --- /dev/null +++ b/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_Martha_Rico_V2.jl @@ -0,0 +1,162 @@ +# Autor: Martha Rico Diener +# Equipo: Los cantantes +# Este programa procesa archivos CSV que contienen calificaciones de películas y +# estadísticas sobre géneros. Utiliza concurrencia para manejar múltiples archivos +# de calificaciones y calcula el promedio de calificaciones por género. + +# Instalar los paquetes necesarios si no están instalados +using Pkg +# Verifica si los paquetes "CSV" y "DataFrames" están instalados +if !haskey(Pkg.installed(), "CSV") + Pkg.add("CSV") # Agrega el paquete si no está instalado +else + println("El paquete 'CSV' ya está instalado.") +end +if !haskey(Pkg.installed(), "DataFrames") + Pkg.add("DataFrames") # Agrega el paquete si no está instalado +else + println("El paquete 'DataFrames' ya está instalado.") +end +using Pkg +Pkg.add("CSV") +Pkg.add("DataFrames") +using CSV +using DataFrames +using Base.Threads +using Printf + +# Tiempo de inicio +t1 = time() #Lo utilizo para calcular el tiempo total de ejecución. + +# Cargar el archivo CSV en un DataFrame +df = CSV.File("ratings.csv") |> DataFrame + +# Definir el número de partes en las que quieres dividir el archivo. +# Hacemos esto para poder trabajar ls archivos resultantes en paralelo. +N = 10 + +# Calcular el tamaño de cada parte +num_rows = nrow(df) +rows_per_part = div(num_rows, N) + (num_rows % N != 0 ? 1 : 0) # Asegura que todas las filas se procesen + +# Crear un loop para dividir el DataFrame y guardar cada parte +for i in 0:(N-1) + start_row = i * rows_per_part + 1 + end_row = min((i == N - 1) ? num_rows : (start_row + rows_per_part - 1), num_rows) + + if start_row > num_rows + break # Salir si el índice inicial es mayor que el número de filas + end + + # Crear un DataFrame para la parte actual + part_df = df[start_row:end_row, :] + + # Guardar la parte como un nuevo archivo CSV + CSV.write("ratings_part_$(i + 1).csv", part_df) +end + +println("El archivo ha sido dividido en $N partes.") +elapsed_time = time() - t1 +println("Tiempo transcurrido en dividir: ", elapsed_time, " seconds") + +# Asegúrate de que el número de hilos esté configurado +println("Número de hilos disponibles: ", nthreads()) + +# Función para cargar los archivos CSV y contar calificaciones por género +function cargar_y_contar(i, movies) + ratings = CSV.read("ratings_part_" * string(i) * ".csv", DataFrame) + return count_ratings_by_genre(ratings, movies) +end + +# Función para contar las calificaciones por género y calcular el promedio +function count_ratings_by_genre(ratings, movies) + # Unir las tablas por 'movieId' + data = innerjoin(ratings, movies, on=:movieId) + + # Inicializar un diccionario para acumular calificaciones y contar por género + genre_data = Dict{String, Tuple{Float64, Int}}() # (suma de calificaciones, conteo) + + for row in eachrow(data) + genres = split(row.genres, "|") # Dividir los géneros por '|' + rating = row.rating # Suponiendo que la calificación está en la columna 'rating' + + for genre in genres + if haskey(genre_data, genre) + genre_data[genre] = (genre_data[genre][1] + rating, genre_data[genre][2] + 1) + else + genre_data[genre] = (rating, 1) + end + end + end + + return genre_data # Cambiado para devolver el diccionario completo +end + +# Cargar la lista de películas +movies = CSV.read("movies.csv", DataFrame) + +# Arreglo para almacenar los resultados de cada hilo +results = Vector{Dict{String, Tuple{Float64, Int}}}(undef, N) + +# Usar Threads.@threads para ejecutar en paralelo +Threads.@threads for i in 1:N + results[i] = cargar_y_contar(i, movies) +end + +# Combinar los resultados de todos los hilos +final_counts = Dict{String, Tuple{Float64, Int}}() # (suma de calificaciones, conteo total) + +for result in results + for (genre, (sum_ratings, count)) in result + if haskey(final_counts, genre) + final_counts[genre] = (final_counts[genre][1] + sum_ratings, final_counts[genre][2] + count) + else + final_counts[genre] = (sum_ratings, count) + end + end +end +using Printf + +println("Género\t\t\tConteo\tPromedio de calificaciones") +println("----------------------------------------------------------") + +# Función para dividir los núemros grandes con comas +function format_with_commas(n::Int) + s = string(n) + len = length(s) + + if len <= 3 + return s + end + + result = "" + for (i, digit) in enumerate(reverse(s)) + if i > 1 && (i - 1) % 3 == 0 + result *= "," + end + result *= digit + end + + return reverse(result) +end + +# Asegurarse de que final_counts esté definido y no esté vacío +if isempty(final_counts) + println("No se encontraron géneros.") +else + sorted_genres = sort(collect(keys(final_counts))) + + for genre in sorted_genres + (sum_ratings, count) = final_counts[genre] + average = count > 0 ? sum_ratings / count : 0.0 # Evitar división por cero + formated_count = format_with_commas(count) + # println(@sprintf("%-20s\t%s\t\t%.2f", genre, formated_count, average)) + println(@sprintf("%-20s\t%20s\t%10.2f", genre, formated_count, average)) + end +end + +final_time = time() - t1 +println("Tiempo transcurrido total: ", final_time, " seconds") +count_time = final_time - elapsed_time +println("Tiempo transcurrido en contar y promediar: ", count_time, " seconds") + From 33cfc13a517c457809208f7494b5ffd605b2e67b Mon Sep 17 00:00:00 2001 From: Rikkonen Date: Fri, 8 Nov 2024 13:18:03 -0600 Subject: [PATCH 4/4] Delete julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go Lo puse por error en este directorio --- .../DividirContarPromedio_MarthaRico_V2.0.go | 270 ------------------ 1 file changed, 270 deletions(-) delete mode 100644 julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go diff --git a/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go b/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go deleted file mode 100644 index 123eacdd..00000000 --- a/julia/src/student_submissions/movielens/martha_rico/DividirContarPromedio_MarthaRico_V2.0.go +++ /dev/null @@ -1,270 +0,0 @@ -/* Autor Martha Rico Diener -Equipo Los cantantes -Este programa procesa archivos CSV que contienen calificaciones de películas y -estadísticas sobre géneros. Utiliza concurrencia para manejar múltiples archivos -de calificaciones y calcula el promedio de calificaciones por género. -*/ - -package main - -/*Se importan varios paquetes necesarios para la funcionalidad del programa: -encoding/csv para manejar archivos CSV -fmt para imprimir en consola -log para registrar errores -os para operaciones de archivos -sort para ordenar datos, -strings para manipulación de cadenas -sync para gestionar concurrencia -time para medir el tiempo de ejecución. -*/ - -import ( - "encoding/csv" - "fmt" - "log" - "os" - "sort" - "strings" - "sync" - "time" - "strconv" -) - -func dividir() { //Esta función divide el archivo ratings en 10 partes iguales - - startTime := time.Now() //Para medir el tiempo de ejecución - - // Cargar el archivo CSV - df, headers, err := loadCSV("ratings.csv") - if err != nil { - panic(err) - } - - // Definir el número de partes en las que quieres dividir el archivo - N := 10 - - // Calcular el tamaño de cada parte - numRows := len(df) - rowsPerPart := numRows / N - - // Crear un loop para dividir el DataFrame y guardar cada parte - for i := 0; i < N; i++ { - startRow := i * rowsPerPart - endRow := startRow + rowsPerPart - - // Asegurarse de no exceder el número de filas - if i == N-1 { - endRow = numRows - } - - // Crear y guardar la parte como un nuevo archivo CSV - partDF := df[startRow:endRow] - err := saveCSV(fmt.Sprintf("ratings_part_%d.csv", i+1), headers, partDF) - if err != nil { - panic(err) - } - } - - fmt.Printf("El archivo ha sido dividido en %d partes.\n", N) - elapsedTime := time.Since(startTime) - fmt.Printf("Tiempo transcurrido hasta este moemnto: %s\n", elapsedTime) -} - -// loadCSV carga un archivo CSV y devuelve los datos como una matriz de cadenas y los encabezados -func loadCSV(filename string) ([][]string, []string, error) { - file, err := os.Open(filename) - if err != nil { - return nil, nil, err - } - defer file.Close() - - reader := csv.NewReader(file) - records, err := reader.ReadAll() - if err != nil { - return nil, nil, err - } - - // Separar los encabezados de los datos - headers := records[0] - data := records[1:] - - return data, headers, nil -} - -// saveCSV guarda una parte de datos en un nuevo archivo CSV con encabezados -func saveCSV(filename string, headers []string, data [][]string) error { - file, err := os.Create(filename) - if err != nil { - return err - } - defer file.Close() - - writer := csv.NewWriter(file) - defer writer.Flush() - - // Escribir los encabezados en el archivo CSV - if err := writer.Write(headers); err != nil { - return err - } - - // Escribir las filas en el archivo CSV - return writer.WriteAll(data) -} - -/* - **Función `processRatings`**: - - Toma el nombre de un archivo de calificaciones, - un mapa para almacenar estadísticas de géneros, - un mutex para sincronización y un `WaitGroup` - para esperar a que se completen las goroutines. -*/ - -func processRatings(ratingsFile string, genreData map[string]*GenreStats, mu *sync.Mutex, wg *sync.WaitGroup) { - defer wg.Done() - - //Abre el archivo CSV - file, err := os.Open(ratingsFile) - if err != nil { - log.Printf("Error al abrir %s: %v", ratingsFile, err) - return - } - defer file.Close() - - reader := csv.NewReader(file) - ratingsData, err := reader.ReadAll() - if err != nil { - log.Printf("Error al leer %s: %v", ratingsFile, err) - return - } - - for _, rating := range ratingsData[1:] { // Ignorar encabezados - movieId := rating[1] - var ratingValue float64 - if len(rating) > 2 { - _, err := fmt.Sscanf(rating[2], "%f", &ratingValue) // Leer la calificación como float - if err != nil { - log.Printf("Error al leer la calificación: %v", err) - } - } - - // Buscar el título y género correspondiente en moviesData - for _, movie := range moviesData[1:] { // Ignorar encabezados - if movie[0] == movieId { - genres := strings.Split(movie[2], "|") - mu.Lock() - for _, genre := range genres { - if _, exists := genreData[genre]; !exists { - genreData[genre] = &GenreStats{} - } - genreData[genre].Count++ - genreData[genre].Sum += ratingValue - } - mu.Unlock() - break - } - } - } -} - -var moviesData [][]string - -// Estructura para almacenar estadísticas de cada género -type GenreStats struct { - Count int - Sum float64 -} - - -// Función para formatear números con comas -func formatNumber(n int) string { - s := strconv.Itoa(n) - nLen := len(s) - if nLen <= 3 { - return s - } - - var result strings.Builder - for i, digit := range s { - if (nLen-i)%3 == 0 && i != 0 { - result.WriteRune(',') - } - result.WriteRune(digit) - } - return result.String() -} - -func main() { - - startTime := time.Now() //Para medir el tiempo de ejecución - - dividir() //dividir el archivo ratings.csv en 10 - - // Abrir y leer el archivo movies.csv - moviesFile, err := os.Open("movies.csv") - if err != nil { - log.Fatalf("Error al abrir movies.csv: %v", err) - } - defer moviesFile.Close() - - moviesReader := csv.NewReader(moviesFile) - moviesData, err = moviesReader.ReadAll() - if err != nil { - log.Fatalf("Error al leer movies.csv: %v", err) - } - - // Mapa para almacenar estadísticas de calificaciones por género - genreData := make(map[string]*GenreStats) - var mu sync.Mutex - var wg sync.WaitGroup - - // Procesar cada archivo de ratings concurrentemente - for i := 1; i <= 10; i++ { - wg.Add(1) - go processRatings(fmt.Sprintf("ratings_part_%d.csv", i), genreData, &mu, &wg) - } - - wg.Wait() - - // Crear un slice para almacenar los géneros y sus estadísticas - type genreStat struct { - genre string - count int - average float64 - } - - var sortedGenres []genreStat - for genre, stats := range genreData { - average := 0.0 - if stats.Count > 0 { - average = float64(stats.Sum) / float64(stats.Count) - } - sortedGenres = append(sortedGenres, genreStat{genre, stats.Count, average}) - } - - // Ordenar el slice por el nombre del género - sort.Slice(sortedGenres, func(i, j int) bool { - return sortedGenres[i].genre < sortedGenres[j].genre - }) - -/* -// Imprimir los resultados -fmt.Println("Conteo de calificaciones por género:") -for _, gs := range sortedGenres { - fmt.Printf("%s: \t \t %s \t \t %.2f\n", gs.genre, formatNumber(gs.count), gs.average) -} -*/ - -// Imprimir los resultados -fmt.Println() -fmt.Println("Conteo de calificaciones por género:") -fmt.Printf("%-20s|%20s\t|\t%20s\n", "Género", "Conteo", "Promedio de calificaciones") -fmt.Println("----------------------------------------------------------") -for _, gs := range sortedGenres { - // Ajusta los anchos según tus necesidades - fmt.Printf("%-20s| %20s\t|\t%6.2f\n", gs.genre, formatNumber(gs.count), gs.average) -} - - // Calcular y mostrar el tiempo total de ejecución - elapsedTime := time.Since(startTime) - fmt.Printf("Tiempo total de ejecución: %s\n", elapsedTime) -}