/
notuw
/
weather
Обзор
Документация
Войти
/
notuw
/
weather
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
code.txt
1 217 строк
45 KB
notuw
update code.txt
03 июн 2025, 22:38
03 июн 2025, 22:38
7833f21
Код
Авторство
О чём код?
using System; using System.IO; using System.Text.Json; using System.Collections.Generic; using System.Linq; class Program { private static bool ShowTechnicalData = true; public static string JsonFilePath = "./all_forecasts.json"; static void Main(string[] args) { Console.WriteLine("=================================================="); Console.WriteLine(" Программа анализа данных о погоде "); Console.WriteLine(""); Weather weatherService = new Weather(JsonFilePath); while (true) { Console.WriteLine("\n--- ГЛАВНОЕ МЕНЮ ---"); Console.WriteLine("1. Показать сводку данных и прогноз по городу"); Console.WriteLine("2. Управление данными"); Console.WriteLine("3. Статистика"); Console.WriteLine("4. Выход"); Console.Write("Выберите пункт меню (1-4): "); string choice = Console.ReadLine(); switch (choice) { case "1": ShowCityWeatherSummary(weatherService); break; case "2": ManageWeatherData(weatherService); break; case "3": ShowStatisticsMenu(weatherService); break; case "4": Console.WriteLine("До свидания!"); return; default: Console.WriteLine("Неправильный выбор! Попробуйте снова."); break; } } } static void ShowCityWeatherSummary(Weather weather) { Console.Write("Введите название города: "); string cityName = Console.ReadLine(); WeatherData cityData = weather.SearchDataCity(cityName); if (cityData != null) { if (cityData.Forecast == null || cityData.Forecast.time == null || cityData.Forecast.time.Length == 0) { Console.WriteLine("Нет данных о погоде для этого города."); return; } DateTime startDate; DateTime endDate; if (!DateTime.TryParse(cityData.Forecast.time[0], null, out startDate) || !DateTime.TryParse(cityData.Forecast.time[cityData.Forecast.time.Length - 1], null, out endDate)) { Console.WriteLine("Ошибка в формате дат в данных прогноза."); return; } WeatherData dailyAverageData = Weather.GetDailyAverage(cityData, startDate, endDate); if (dailyAverageData == null || dailyAverageData.Forecast == null || dailyAverageData.Forecast.time == null || dailyAverageData.Forecast.time.Length == 0) { Console.WriteLine("Не удалось рассчитать средние дневные данные для указанного периода."); return; } Console.WriteLine($"\n--- Данные по городу: {dailyAverageData.City} (Средние за день) ---"); Console.WriteLine("Дата Температура (°C) Влажность (%) Осадки (мм) Облачность (%)"); Console.WriteLine("--------------------------------------------------------------------------"); List<DateTime> datesWithPrecipitation = new List<DateTime>(); for (int i = 0; i < dailyAverageData.Forecast.time.Length; i++) { if (DateTime.TryParse(dailyAverageData.Forecast.time[i], null, out DateTime date)) { string dateStr = date.ToString("dd.MM.yyyy"); string tempStr = dailyAverageData.Forecast.temperature_2m[i]?.ToString("F1") ?? "N/A"; string humidityStr = dailyAverageData.Forecast.relative_humidity_2m[i]?.ToString("F1") ?? "N/A"; string precipitationStr = dailyAverageData.Forecast.precipitation[i]?.ToString("F1") ?? "N/A"; string cloudCoverStr = dailyAverageData.Forecast.cloud_cover[i]?.ToString("F1") ?? "N/A"; if (dailyAverageData.Forecast.precipitation[i].HasValue && dailyAverageData.Forecast.precipitation[i].Value > 0) { datesWithPrecipitation.Add(date); } Console.WriteLine($"{dateStr,-10} {tempStr,-19} {humidityStr,-16} {precipitationStr,-14} {cloudCoverStr,-15}"); } else { Console.WriteLine($"Ошибка формата даты: {dailyAverageData.Forecast.time[i]}"); } } if (datesWithPrecipitation.Any()) { Console.Write("\n* - Осадки были зафиксированы на следующие даты: "); Console.WriteLine(string.Join(", ", datesWithPrecipitation.Select(d => d.ToString("dd.MM")))); } Console.WriteLine("\n--- Подменю ---"); Console.WriteLine("0 - Выйти в главное меню"); Console.WriteLine("1 - Представить температуру в виде графика"); Console.Write("Ваш выбор: "); string subChoice = Console.ReadLine(); switch (subChoice) { case "0": break; case "1": ConsoleChart.DrawTemperature(dailyAverageData, ShowTechnicalData); break; default: Console.WriteLine("Неправильный выбор! Возврат в главное меню."); break; } } } static void ManageWeatherData(Weather weather) { Console.WriteLine("\n--- УПРАВЛЕНИЕ ДАННЫМИ ---"); Console.WriteLine($"Всего городов в файле: {weather.allData.Length}"); Console.WriteLine("0 - Выйти в главное меню"); Console.WriteLine("1 - Проверить и удалить дубликаты"); Console.Write("Ваш выбор: "); string choice = Console.ReadLine(); switch (choice) { case "0": break; case "1": Console.WriteLine("Желаете ли вы удалить повторы из JSON? (да/нет)"); string confirm = Console.ReadLine()?.ToLower() ?? "нет"; if (confirm == "да") { int duplicatesRemoved = weather.RemoveDuplicateCities(JsonFilePath); Console.WriteLine($"Удалено {duplicatesRemoved} дубликатов городов."); } else { Console.WriteLine("Удаление дубликатов отменено."); } break; default: Console.WriteLine("Неправильный выбор! Возврат в главное меню."); break; } } static void ShowStatisticsMenu(Weather weather) { Statistic stats = new Statistic(); stats.Init(weather.allData); while (true) { Console.WriteLine("\n--- МЕНЮ СТАТИСТИКИ ---"); Console.WriteLine("1. Город с наиболее точным предсказанием погоды (R^2)"); Console.WriteLine("2. Город с самым неточным предсказанием погоды (R^2)"); Console.WriteLine("3. Город с самыми низкими температурами"); Console.WriteLine("4. Город с самыми высокими температурами"); Console.WriteLine("5. Случайный город"); Console.WriteLine("0. Выйти в главное меню"); Console.Write("Выберите пункт меню (0-5): "); string choice = Console.ReadLine(); switch (choice) { case "1": stats.FindMostAccurateCity(); break; case "2": stats.FindLeastAccurateCity(); break; case "3": stats.FindCityWithLowestTemperature(); break; case "4": stats.FindCityWithHighestTemperature(); break; case "5": stats.FindRandomCity(); break; case "0": return; default: Console.WriteLine("Неправильный выбор! Попробуйте снова."); break; } } } } public class Weather { public WeatherData[] allData { get; private set; } public Weather(string path) { LoadData(path); } private void LoadData(string path) { try { var jsonString = File.ReadAllText(path); var data = JsonSerializer.Deserialize<WeatherData[]>(jsonString); if (data == null) { Console.WriteLine("Нет данных."); allData = Array.Empty<WeatherData>(); } else { allData = data; } } catch (FileNotFoundException) { Console.WriteLine($"Файл не найден: {path}"); allData = Array.Empty<WeatherData>(); } catch (JsonException ex) { Console.WriteLine($"Ошибка при разборе JSON: {ex.Message}"); allData = Array.Empty<WeatherData>(); } catch (Exception ex) { Console.WriteLine($"Произошла ошибка при загрузке данных: {ex.Message}"); allData = Array.Empty<WeatherData>(); } } public WeatherData SearchDataCity(string name) { if (string.IsNullOrWhiteSpace(name)) { Console.WriteLine("Название города не может быть пустым."); return null; } string transliteratedName = ToEnglish(name); foreach (var cityData in allData) { if (cityData.City.Equals(name, StringComparison.OrdinalIgnoreCase) || cityData.City.Equals(transliteratedName, StringComparison.OrdinalIgnoreCase)) { return cityData; } } List<WeatherData> searchedCities = new List<WeatherData>(); string currentSearch = transliteratedName; while (currentSearch.Length > 0) { foreach (var cityData in allData) { if (cityData.City.StartsWith(currentSearch, StringComparison.OrdinalIgnoreCase)) { if (!searchedCities.Any(c => c.City.Equals(cityData.City, StringComparison.OrdinalIgnoreCase))) { searchedCities.Add(cityData); } } } if (searchedCities.Any()) { break; } currentSearch = currentSearch.Substring(0, currentSearch.Length - 1); } if (!searchedCities.Any()) { currentSearch = name; while (currentSearch.Length > 0) { foreach (var cityData in allData) { if (cityData.City.StartsWith(currentSearch, StringComparison.OrdinalIgnoreCase)) { if (!searchedCities.Any(c => c.City.Equals(cityData.City, StringComparison.OrdinalIgnoreCase))) { searchedCities.Add(cityData); } } } if (searchedCities.Any()) { break; } currentSearch = currentSearch.Substring(0, currentSearch.Length - 1); } } if (searchedCities.Any()) { Console.WriteLine("Возможно вы имели ввиду один из этих городов:"); for (int i = 0; i < searchedCities.Count; i++) { Console.WriteLine($"{i} : {searchedCities[i].City}"); } Console.WriteLine("Введите один из предложенных номеров или нажмите ENTER для выхода."); string select = Console.ReadLine(); if (int.TryParse(select, out int select_digit) && select_digit >= 0 && select_digit < searchedCities.Count) { return searchedCities[select_digit]; } else { Console.WriteLine("Записи о городе отсутствуют или выбран неверный номер."); return null; } } Console.WriteLine("Записи о городе отсутствуют."); return null; } private string ToEnglish(string text) { var map = new Dictionary<char, string> { {'а', "a"}, {'б', "b"}, {'в', "v"}, {'г', "g"}, {'д', "d"}, {'е', "e"}, {'ё', "yo"}, {'ж', "zh"}, {'з', "z"}, {'и', "i"}, {'й', "y"}, {'к', "k"}, {'л', "l"}, {'м', "m"}, {'н', "n"}, {'о', "o"}, {'п', "p"}, {'р', "r"}, {'с', "s"}, {'т', "t"}, {'у', "u"}, {'ф', "f"}, {'х', "kh"}, {'ц', "ts"}, {'ч', "ch"}, {'ш', "sh"}, {'щ', "sch"}, {'ъ', ""}, {'ы', "y"}, {'ь', ""}, {'э', "e"}, {'ю', "yu"}, {'я', "ya"}, {'А', "A"}, {'Б', "B"}, {'В', "V"}, {'Г', "G"}, {'Д', "D"}, {'Е', "E"}, {'Ё', "Yo"}, {'Ж', "Zh"}, {'З', "Z"}, {'И', "I"}, {'Й', "Y"}, {'К', "K"}, {'Л', "L"}, {'М', "M"}, {'Н', "N"}, {'О', "O"}, {'П', "P"}, {'Р', "R"}, {'С', "S"}, {'Т', "T"}, {'У', "U"}, {'Ф', "F"}, {'Х', "Kh"}, {'Ц', "Ts"}, {'Ч', "Ch"}, {'Ш', "Sh"}, {'Щ', "Sch"}, {'Ъ', ""}, {'Ы', "Y"}, {'Ь', ""}, {'Э', "E"}, {'Ю', "Yu"}, {'Я', "Ya"} }; return string.Concat(text.Select(c => map.ContainsKey(c) ? map[c] : c.ToString())); } public int RemoveDuplicateCities(string path) { if (allData == null || !allData.Any()) return 0; var distinctCities = new Dictionary<string, WeatherData>(StringComparer.OrdinalIgnoreCase); int duplicatesCount = 0; foreach (var data in allData) { if (distinctCities.ContainsKey(data.City)) { duplicatesCount++; } else { distinctCities.Add(data.City, data); } } if (duplicatesCount > 0) { allData = distinctCities.Values.ToArray(); try { var options = new JsonSerializerOptions { WriteIndented = true }; string updatedJson = JsonSerializer.Serialize(allData, options); File.WriteAllText(path, updatedJson); Console.WriteLine("Дубликаты удалены и файл JSON обновлен."); } catch (Exception ex) { Console.WriteLine($"Ошибка при сохранении файла: {ex.Message}"); } } else { Console.WriteLine("Дубликатов не найдено."); } return duplicatesCount; } public static WeatherData GetPeriod(WeatherData weatherData, DateTime start, DateTime end) { if (weatherData?.Forecast?.time == null) return null; var filteredIndices = new List<int>(); for (int i = 0; i < weatherData.Forecast.time.Length; i++) { if (DateTime.TryParse(weatherData.Forecast.time[i], null, out DateTime dateTime)) { if (dateTime >= start && dateTime <= end) { filteredIndices.Add(i); } } } int count = filteredIndices.Count; if (count == 0) return null; string[] newTime = new string[count]; double?[] newTemp = new double?[count]; double?[] newHumidity = new double?[count]; double?[] newDewPoint = new double?[count]; double?[] newPrecipitation = new double?[count]; double?[] newCloudCover = new double?[count]; for (int j = 0; j < count; j++) { int originalIndex = filteredIndices[j]; newTime[j] = weatherData.Forecast.time[originalIndex]; newTemp[j] = weatherData.Forecast.temperature_2m[originalIndex]; newHumidity[j] = weatherData.Forecast.relative_humidity_2m[originalIndex]; newDewPoint[j] = weatherData.Forecast.dew_point_2m[originalIndex]; newPrecipitation[j] = weatherData.Forecast.precipitation[originalIndex]; newCloudCover[j] = weatherData.Forecast.cloud_cover[originalIndex]; } return new WeatherData { City = weatherData.City, Forecast = new Forecast { time = newTime, temperature_2m = newTemp, relative_humidity_2m = newHumidity, dew_point_2m = newDewPoint, precipitation = newPrecipitation, cloud_cover = newCloudCover } }; } public static WeatherData GetDailyAverage(WeatherData weatherData, DateTime start, DateTime end) { WeatherData periodData = GetPeriod(weatherData, start, end); if (periodData?.Forecast?.time == null || periodData.Forecast.time.Length == 0) return null; var dailyGroups = new Dictionary<string, List<int>>(); for (int i = 0; i < periodData.Forecast.time.Length; i++) { if (DateTime.TryParse(periodData.Forecast.time[i], null, out DateTime dateTime)) { string dayKey = dateTime.ToString("yyyy-MM-dd"); if (!dailyGroups.ContainsKey(dayKey)) { dailyGroups[dayKey] = new List<int>(); } dailyGroups[dayKey].Add(i); } } if (dailyGroups.Count == 0) return null; List<string> dayKeysList = new List<string>(dailyGroups.Keys); dayKeysList.Sort(StringComparer.Ordinal); string[] sortedDays = dayKeysList.ToArray(); int dayCount = sortedDays.Length; string[] newTime = new string[dayCount]; double?[] newTemp = new double?[dayCount]; double?[] newHumidity = new double?[dayCount]; double?[] newDewPoint = new double?[dayCount]; double?[] newPrecipitation = new double?[dayCount]; double?[] newCloudCover = new double?[dayCount]; for (int dayIndex = 0; dayIndex < dayCount; dayIndex++) { string currentDayKey = sortedDays[dayIndex]; var indicesForCurrentDay = dailyGroups[currentDayKey]; newTime[dayIndex] = currentDayKey; newTemp[dayIndex] = CalculateAverage(periodData.Forecast.temperature_2m, indicesForCurrentDay); newHumidity[dayIndex] = CalculateAverage(periodData.Forecast.relative_humidity_2m, indicesForCurrentDay); newDewPoint[dayIndex] = CalculateAverage(periodData.Forecast.dew_point_2m, indicesForCurrentDay); newPrecipitation[dayIndex] = CalculateSum(periodData.Forecast.precipitation, indicesForCurrentDay); newCloudCover[dayIndex] = CalculateAverage(periodData.Forecast.cloud_cover, indicesForCurrentDay); } return new WeatherData { City = periodData.City, Forecast = new Forecast { time = newTime, temperature_2m = newTemp, relative_humidity_2m = newHumidity, dew_point_2m = newDewPoint, precipitation = newPrecipitation, cloud_cover = newCloudCover } }; } private static double? CalculateAverage(double?[] array, List<int> indices) { if (array == null || indices == null || !indices.Any()) return null; double sum = 0; int count = 0; foreach (int index in indices) { if (index < array.Length && array[index].HasValue) { sum += array[index].Value; count++; } } return count > 0 ? sum / count : null; } private static double? CalculateSum(double?[] array, List<int> indices) { if (array == null || indices == null || !indices.Any()) return null; double sum = 0; int countNonNull = 0; foreach (int index in indices) { if (index < array.Length && array[index].HasValue) { sum += array[index].Value; countNonNull++; } } return countNonNull > 0 ? sum : null; } } public class LinearRegression { public double Slope { get; private set; } public double Intercept { get; private set; } public double RSquared { get; private set; } public void Calculate(double[] x, double[] y) { if (x == null || y == null || x.Length != y.Length) { Slope = 0; Intercept = 0; RSquared = 0; Console.WriteLine("Ошибка: массивы для регрессии недействительны или имеют разную длину."); return; } int n = x.Length; if (n < 2) { Slope = 0; Intercept = y.Length > 0 ? y.Average() : 0; RSquared = 0; return; } double sumX = 0, sumY = 0, sumXY = 0, sumXX = 0; for (int i = 0; i < n; i++) { sumX += x[i]; sumY += y[i]; sumXY += x[i] * y[i]; sumXX += x[i] * x[i]; } double denominator = (n * sumXX - sumX * sumX); if (Math.Abs(denominator) < 1e-9) { Slope = 0; Intercept = sumY / n; } else { Slope = (n * sumXY - sumX * sumY) / denominator; Intercept = (sumY - Slope * sumX) / n; } CalculateRSquared(x, y); } private void CalculateRSquared(double[] x, double[] y) { if (y.Length == 0) { RSquared = 0; return; } double meanY = y.Average(); if (double.IsNaN(meanY) || double.IsInfinity(meanY)) { RSquared = 0; return; } double totalSumSquares = 0; double residualSumSquares = 0; for (int i = 0; i < y.Length; i++) { double predicted = Predict(x[i]); totalSumSquares += Math.Pow(y[i] - meanY, 2); residualSumSquares += Math.Pow(y[i] - predicted, 2); } if (Math.Abs(totalSumSquares) < 1e-9) { RSquared = (Math.Abs(residualSumSquares) < 1e-9) ? 1 : 0; } else { RSquared = 1 - (residualSumSquares / totalSumSquares); } if (double.IsNaN(RSquared)) RSquared = 0; } public double Predict(double xValue) { return Slope * xValue + Intercept; } public string GetEquation() { string sign = Intercept >= 0 ? "+" : ""; return $"y = {Slope.ToString("F4")}x {sign} {Intercept.ToString("F4")}"; } } public class Chart { private string[] cleanTime; private double[] cleanValues; private int[] normalizedValues; private int[] normalizedForecast; private double minValue, maxValue, avgValue; private int forecastDays = 10; private LinearRegression regressionModel; public void Draw(string[] time, double?[] values, string title, string unit, bool showTechnical = false) { if (values == null || values.Length == 0 || time == null || time.Length != values.Length) { Console.WriteLine("Нет данных для отображения или данные некорректны."); return; } CleanUpData(time, values); if (cleanValues.Length == 0) { Console.WriteLine("Нет корректных данных для отображения после очистки."); return; } CalculateStatistics(); double[] forecastArray = Array.Empty<double>(); if (cleanValues.Length >= 2) { forecastArray = CalculateLinearForecast(); if (forecastArray.Length > 0) { UpdateStatsWithForecast(forecastArray); } } NormalizeActualValues(); if (forecastArray.Length > 0) { NormalizeForecastValues(forecastArray); } PrintChartToConsole(title, unit, forecastArray, showTechnical); } private void CleanUpData(string[] time, double?[] values) { List<string> tempTimeList = new List<string>(); List<double> tempValueList = new List<double>(); for (int i = 0; i < values.Length; i++) { if (values[i].HasValue) { tempTimeList.Add(time[i]); tempValueList.Add(values[i].Value); } } cleanTime = tempTimeList.ToArray(); cleanValues = tempValueList.ToArray(); } private void CalculateStatistics() { if (cleanValues.Length == 0) return; minValue = cleanValues[0]; maxValue = cleanValues[0]; double sum = 0; for (int i = 0; i < cleanValues.Length; i++) { if (cleanValues[i] < minValue) minValue = cleanValues[i]; if (cleanValues[i] > maxValue) maxValue = cleanValues[i]; sum += cleanValues[i]; } avgValue = sum / cleanValues.Length; } private double[] CalculateLinearForecast() { if (cleanValues.Length < 2) return Array.Empty<double>(); regressionModel = new LinearRegression(); double[] xData = new double[cleanValues.Length]; for (int i = 0; i < xData.Length; i++) { xData[i] = i; } regressionModel.Calculate(xData, cleanValues); double[] forecast = new double[forecastDays]; for (int i = 0; i < forecastDays; i++) { forecast[i] = regressionModel.Predict(cleanValues.Length + i); } return forecast; } private void UpdateStatsWithForecast(double[] forecast) { foreach (double val in forecast) { if (val < minValue) minValue = val; if (val > maxValue) maxValue = val; } } private void NormalizeActualValues() { normalizedValues = new int[cleanValues.Length]; double range = maxValue - minValue; if (Math.Abs(range) < 1e-9) { for (int i = 0; i < cleanValues.Length; i++) normalizedValues[i] = 7; } else { for (int i = 0; i < cleanValues.Length; i++) { normalizedValues[i] = (int)Math.Round((cleanValues[i] - minValue) / range * 15); normalizedValues[i] = Math.Max(0, Math.Min(15, normalizedValues[i])); } } } private void NormalizeForecastValues(double[] forecast) { normalizedForecast = new int[forecast.Length]; double range = maxValue - minValue; if (Math.Abs(range) < 1e-9) { for (int i = 0; i < forecast.Length; i++) normalizedForecast[i] = 7; } else { for (int i = 0; i < forecast.Length; i++) { normalizedForecast[i] = (int)Math.Round((forecast[i] - minValue) / range * 15); normalizedForecast[i] = Math.Max(0, Math.Min(15, normalizedForecast[i])); } } } private void PrintChartToConsole(string title, string unit, double[] forecast, bool showTechnical) { Console.WriteLine($"\n{title}"); Console.WriteLine(new string('=', title.Length)); Console.WriteLine("█ - Фактические данные, ░ - Прогноз по линии"); if (showTechnical && regressionModel != null && cleanValues.Length >= 2) PrintRegressionTechnicalInfo(); int maxChartColumns = 50; int actualDataPointsToDisplay = cleanValues.Length; int forecastPointsToDisplay = forecast.Length; if (actualDataPointsToDisplay + forecastPointsToDisplay > maxChartColumns) { if (forecastPointsToDisplay >= maxChartColumns) { forecastPointsToDisplay = maxChartColumns; actualDataPointsToDisplay = 0; } else { actualDataPointsToDisplay = maxChartColumns - forecastPointsToDisplay; actualDataPointsToDisplay = Math.Min(actualDataPointsToDisplay, cleanValues.Length); } } int startActualIndex = Math.Max(0, cleanValues.Length - actualDataPointsToDisplay); for (int row = 15; row >= 0; row--) { double levelValue = minValue + (maxValue - minValue) * row / 15.0; Console.Write($"{levelValue.ToString("F1"),6}{unit}|"); for (int colIdx = 0; colIdx < actualDataPointsToDisplay; colIdx++) { int actualDataIndex = startActualIndex + colIdx; if (actualDataIndex < normalizedValues.Length) { Console.Write(normalizedValues[actualDataIndex] >= row ? "█" : " "); } else { Console.Write(" "); } } for (int col = 0; col < forecastPointsToDisplay; col++) { if (normalizedForecast != null && normalizedForecast.Length > col) { Console.Write(normalizedForecast[col] >= row ? "░" : " "); } else { Console.Write(" "); } } Console.WriteLine(); } Console.Write(new string('-', 8 + actualDataPointsToDisplay + forecastPointsToDisplay) + "\n"); PrintOverallStatistics(unit); if (cleanTime != null && cleanTime.Length > 0 && actualDataPointsToDisplay > 0) { string firstDateStrToDisplay = cleanTime[startActualIndex]; string lastDateStrToDisplay = cleanTime[startActualIndex + actualDataPointsToDisplay - 1]; string formattedStartDate = firstDateStrToDisplay; string formattedEndDate = lastDateStrToDisplay; if (DateTime.TryParse(firstDateStrToDisplay, null, out DateTime parsedFirstDate)) { formattedStartDate = parsedFirstDate.ToString("dd.MM.yyyy"); } if (DateTime.TryParse(lastDateStrToDisplay, null, out DateTime parsedLastDate)) { formattedEndDate = parsedLastDate.ToString("dd.MM.yyyy"); } if (actualDataPointsToDisplay < cleanValues.Length) { Console.WriteLine($"Отображены данные с {formattedStartDate} по {formattedEndDate} (последние {actualDataPointsToDisplay} из {cleanValues.Length})"); } else { Console.WriteLine($"Данные с {formattedStartDate} по {formattedEndDate}"); } } else if (actualDataPointsToDisplay == 0 && forecastPointsToDisplay > 0) { Console.WriteLine("Отображен только прогноз."); } if (forecast.Length > 0) { Console.WriteLine($"Прогноз на {forecastDays} дня(ей) вперед отмечен символом ░"); } } private void PrintOverallStatistics(string unit) { Console.WriteLine($"Мин: {minValue.ToString("F1")}{unit}, Макс: {maxValue.ToString("F1")}{unit}, Средн: {avgValue.ToString("F1")}{unit}, Точек: {cleanValues.Length}"); } private void PrintRegressionTechnicalInfo() { if (regressionModel != null) { Console.WriteLine($"Уравнение регрессии: {regressionModel.GetEquation()}"); Console.WriteLine($"Коэффициент детерминации (R²): {regressionModel.RSquared.ToString("F4")} ({regressionModel.RSquared.ToString("P1")})"); if (regressionModel.RSquared > 0.7) Console.WriteLine("Комментарий: модель хорошо описывает данные."); else if (regressionModel.RSquared > 0.4) Console.WriteLine("Комментарий: модель средне описывает данные."); else Console.WriteLine("Комментарий: модель плохо описывает данные (низкая предсказательная сила)."); } } } public static class ConsoleChart { private static Chart chartDrawer = new Chart(); public static void DrawTemperature(WeatherData data, bool showTechnical = false) { if (data?.Forecast?.temperature_2m == null) { Console.WriteLine("Нет данных по температуре для этого города."); return; } chartDrawer.Draw(data.Forecast.time, data.Forecast.temperature_2m, $"Температура для города {data.City}", "°C", showTechnical); } } public class Statistic { WeatherData[] allWeatherData; private Random random = new Random(); public void Init(WeatherData[] data) { allWeatherData = data; } private WeatherData GetDailyAverageForStats(WeatherData cityData) { if (cityData?.Forecast?.time == null || cityData.Forecast.time.Length == 0) return null; if (!DateTime.TryParse(cityData.Forecast.time[0], null, out DateTime startDate) || !DateTime.TryParse(cityData.Forecast.time[cityData.Forecast.time.Length - 1], null, out DateTime endDate)) { return null; } return Weather.GetDailyAverage(cityData, startDate, endDate); } public void FindMostAccurateCity() { if (allWeatherData == null || !allWeatherData.Any()) { Console.WriteLine("Нет данных для анализа."); return; } double maxRSquared = -1.0; string mostAccurateCity = "N/A"; foreach (var cityData in allWeatherData) { WeatherData dailyData = GetDailyAverageForStats(cityData); if (dailyData?.Forecast?.temperature_2m == null || dailyData.Forecast.temperature_2m.Length < 2) { continue; } List<double> cleanTemps = new List<double>(); List<double> xData = new List<double>(); for (int i = 0; i < dailyData.Forecast.temperature_2m.Length; i++) { if (dailyData.Forecast.temperature_2m[i].HasValue) { cleanTemps.Add(dailyData.Forecast.temperature_2m[i].Value); xData.Add(i); } } if (cleanTemps.Count < 2) continue; LinearRegression lr = new LinearRegression(); lr.Calculate(xData.ToArray(), cleanTemps.ToArray()); if (lr.RSquared > maxRSquared) { maxRSquared = lr.RSquared; mostAccurateCity = cityData.City; } } Console.WriteLine($"\nГород с наиболее точным предсказанием (R^2): {mostAccurateCity} (R² = {maxRSquared.ToString("F4")})"); } public void FindLeastAccurateCity() { if (allWeatherData == null || !allWeatherData.Any()) { Console.WriteLine("Нет данных для анализа."); return; } double minRSquared = 2.0; string leastAccurateCity = "N/A"; bool foundAny = false; foreach (var cityData in allWeatherData) { WeatherData dailyData = GetDailyAverageForStats(cityData); if (dailyData?.Forecast?.temperature_2m == null || dailyData.Forecast.temperature_2m.Length < 2) { continue; } List<double> cleanTemps = new List<double>(); List<double> xData = new List<double>(); for (int i = 0; i < dailyData.Forecast.temperature_2m.Length; i++) { if (dailyData.Forecast.temperature_2m[i].HasValue) { cleanTemps.Add(dailyData.Forecast.temperature_2m[i].Value); xData.Add(i); } } if (cleanTemps.Count < 2) continue; LinearRegression lr = new LinearRegression(); lr.Calculate(xData.ToArray(), cleanTemps.ToArray()); if (!double.IsNaN(lr.RSquared)) { if (lr.RSquared < minRSquared) { minRSquared = lr.RSquared; leastAccurateCity = cityData.City; } foundAny = true; } } if (!foundAny) Console.WriteLine("\nНе удалось рассчитать R^2 ни для одного города."); else Console.WriteLine($"\nГород с самым неточным предсказанием (R^2): {leastAccurateCity} (R² = {minRSquared.ToString("F4")})"); } public void FindCityWithLowestTemperature() { if (allWeatherData == null || !allWeatherData.Any()) { Console.WriteLine("Нет данных для анализа."); return; } double MinTemp = double.MaxValue; string cityWithMinTemp = "N/A"; string dateOfMinTemp = "N/A"; foreach (var cityData in allWeatherData) { WeatherData dailyData = GetDailyAverageForStats(cityData); if (dailyData?.Forecast?.temperature_2m == null) continue; for (int i = 0; i < dailyData.Forecast.temperature_2m.Length; i++) { if (dailyData.Forecast.temperature_2m[i].HasValue && dailyData.Forecast.temperature_2m[i].Value < MinTemp) { MinTemp = dailyData.Forecast.temperature_2m[i].Value; cityWithMinTemp = cityData.City; // или dailyData.City, они должны быть одинаковы dateOfMinTemp = dailyData.Forecast.time[i]; // Это "yyyy-MM-dd" } } } if (cityWithMinTemp == "N/A") { Console.WriteLine("\nНе удалось найти данные о минимальной температуре."); } else { // Для отображения даты парсим "yyyy-MM-dd" и форматируем в "dd.MM.yyyy" string formattedDate = dateOfMinTemp; if (DateTime.TryParse(dateOfMinTemp, null, out DateTime parsedDate)) { formattedDate = parsedDate.ToString("dd.MM.yyyy"); } Console.WriteLine($"\nГород с самыми низкими температурами: {cityWithMinTemp} ({MinTemp.ToString("F1")}°C на {formattedDate})"); } } public void FindCityWithHighestTemperature() { if (allWeatherData == null || !allWeatherData.Any()) { Console.WriteLine("Нет данных для анализа."); return; } double MaxTemp = double.MinValue; string cityWithMaxTemp = "N/A"; string dateOfMaxTemp = "N/A"; foreach (var cityData in allWeatherData) { WeatherData dailyData = GetDailyAverageForStats(cityData); if (dailyData?.Forecast?.temperature_2m == null) continue; for (int i = 0; i < dailyData.Forecast.temperature_2m.Length; i++) { if (dailyData.Forecast.temperature_2m[i].HasValue && dailyData.Forecast.temperature_2m[i].Value > MaxTemp) { MaxTemp = dailyData.Forecast.temperature_2m[i].Value; cityWithMaxTemp = cityData.City; dateOfMaxTemp = dailyData.Forecast.time[i]; } } } if (cityWithMaxTemp == "N/A") { Console.WriteLine("\nНе удалось найти данные о максимальной температуре."); } else { string formattedDate = dateOfMaxTemp; if (DateTime.TryParse(dateOfMaxTemp, null, out DateTime parsedDate)) { formattedDate = parsedDate.ToString("dd.MM.yyyy"); } Console.WriteLine($"\nГород с самыми высокими температурами: {cityWithMaxTemp} ({MaxTemp.ToString("F1")}°C на {formattedDate})"); } } public void FindRandomCity() { if (allWeatherData == null || !allWeatherData.Any()) { Console.WriteLine("Нет данных для анализа."); return; } int randomIndex = random.Next(0, allWeatherData.Length); WeatherData randomCityData = allWeatherData[randomIndex]; Console.WriteLine($"\nСлучайный город: {randomCityData.City}"); WeatherData dailyData = GetDailyAverageForStats(randomCityData); if (dailyData?.Forecast?.time != null && dailyData.Forecast.time.Any()) { string firstDay = dailyData.Forecast.time.First(); string lastDay = dailyData.Forecast.time.Last(); string formattedStartDate = firstDay; string formattedEndDate = lastDay; if (DateTime.TryParse(firstDay, null, out DateTime parsedStart)) formattedStartDate = parsedStart.ToString("dd.MM.yyyy"); if (DateTime.TryParse(lastDay, null, out DateTime parsedEnd)) formattedEndDate = parsedEnd.ToString("dd.MM.yyyy"); Console.WriteLine($"Данные доступны с {formattedStartDate} по {formattedEndDate}"); double sumTemps = 0; int countTemps = 0; if (dailyData.Forecast.temperature_2m != null) { foreach (var temp in dailyData.Forecast.temperature_2m) { if (temp.HasValue) { sumTemps += temp.Value; countTemps++; } } } if (countTemps > 0) { Console.WriteLine($"Средняя температура за период: {(sumTemps / countTemps).ToString("F1")}°C"); } else { Console.WriteLine("Нет данных о температуре для расчета средней."); } } else { Console.WriteLine("Нет данных о погоде для этого города (после усреднения по дням)."); } } } public class Forecast { public string[] time { get; set; } public double?[] temperature_2m { get; set; } public double?[] relative_humidity_2m { get; set; } public double?[] dew_point_2m { get; set; } public double?[] precipitation { get; set; } public double?[] cloud_cover { get; set; } } public class WeatherData { public string City { get; set; } public Forecast Forecast { get; set; } }