Ви колись уявляли, що займаєтесь наукою про дані у вашій улюбленій мові програмування? Або просто хотіли створити прототип вашого коду з належною візуальною підтримкою? Або навчати та документувати приклади коду? Або просто вивчати мови чи бібліотеки?
Виявляється, є інструмент для цієї роботи, який називається Jupyter Notebook. Однак довгий час його асоціювали тільки з Python, оскільки він був створений для цієї мови. Але тепер усе змінилося, оскільки Polyglot Notebook змінює ситуацію.
Polyglot Notebook дозволяє використовувати не лише Python, R, C#, F#, JS, SQL, KQL, HTML та інші мови, але й об'єднує їх під єдиним дахом!
Polyglot Notebook — це розширення для VS Code від команди .NET Interactive Microsoft, яке дозволяє використовувати найпопулярніші мови в Jupyter notebook.
Ось кілька основних особливостей.
- Підтримка багатьох мов/ядр:
Як вже згадувалося, ви можете використовувати кілька мов в одному Polyglot notebook. Але не тільки це, ви також можете підключатися до інших Python ядер через URL.
Це не тільки дозволяє використовувати єдиний редактор коду скрізь, але й об'єднує кілька ядер в одному notebook.
- Перехресне спілкування між мовами/комірками/ядрами:
Створюйте змінну в Python і одразу передавайте її в C#, і навпаки. Ця функція доступна для багатьох мов у notebook, таких як Python, C#, F#, JS, R, SQL та інші. Як розробник, це не тільки дозволяє вам використовувати правильну мову для правильної задачі, але й відкриває можливість досліджувати область науки про дані у вашій улюбленій мові програмування.
Наприклад: завантажте ваш CSV у JS, очистіть його в C#, візуалізуйте в R і тренуйте модель AI у Python, все це в одному Jupyter notebook з чудовим користувацьким досвідом.
- Jupyter для мов, окрім Python:
Використовуючи Polyglot, тепер ви можете створювати notebook на JS, C#, F# тощо. Легкі приклади коду та документація. Повний досвід Jupyter, адаптований до вашої бажаної мови — це справжній прорив.
Це не тільки дозволяє спільноті писати notebook на їхній мові, але й відкриває двері для нових розробників, щоб вони могли використовувати ці мови в галузі AI без обмежень інструментів. Іншими словами, тепер для кожної підтримуваної мови є рівні умови для AI/науки про дані.
- Використовує файл .ipynb
Іншими словами, це Jupyter notebook, а не якийсь інший формат файлу. Поділіться ним, переглядайте і виконуйте комірки.
- Інтерактивні візуалізації
З C#, JS або Mermaid можна створювати інтерактивні візуалізації. Ми також розглянемо це на демонстрації.
Як почати:
Необхідні умови:
- Остання стабільна версія dotnet sdk (на даний момент .NET 7)
- Для використання Python потрібно встановити Python і jupyter notebook (модуль "jupyterlab").
Перейдіть до VS Code і встановіть розширення Polyglot Notebook.
Створіть перший notebook, створивши файл .ipynb і вибравши “.NET Interactive” як ядро.
Напишіть код у комірці, змініть ядро та поділіться змінними між різними ядрами.
Щоб підключитися до Python, дотримуйтесь інструкцій за цим посиланням
interactive/docs/jupyter-in-polyglot-notebooks.md at main · dotnet/interactive · GitHub
Демо Notebook:
Для цього демонстраційного прикладу я буду використовувати тільки C# і Python. Але ви можете використовувати JS та інші мови за потребою.
Весь код можна знайти на моєму GitHub тут.
Завантажте набір даних за цим посиланням:
MOVIES DATASET FOR FEATURE EXTRACTION, PREDICTION (kaggle.com)
Розпочнемо з створення нотатника та простого зміни ядра на .NET Interactive. Далі завантажимо дані за допомогою старого доброго Python і pandas. Але спочатку підключимося до ядра Python.
#!connect jupyter --kernel-name pythonkernel --kernel-spec python3
Тепер завантажимо набір даних за допомогою Python та pandas (я імпортував json для майбутнього використання в наступних комірках).
Додайте нову комірку. Переконайтеся, що вибране ядро — pythonkernel (воно з’являється в нижньому правому куті комірки).
якщо ні, змініть його на python, просто клацнувши на поточне ядро та вибравши python.
import pandas as pd
import json
Далі завантажимо дані в новій комірці:
df = pd.read_csv("movies.csv")
Тепер ви можете досліджувати дані за допомогою Python, або ж зробимо це в C#?
На поточному етапі ці дані мають багато значень null, і щоб поділитися ними без проблем, давайте перетворимо їх у чистий список словників для передачі в C#.
У новій комірці (pythonkernel):
movies_json = json.loads(df.to_json(orient='records'))
Тепер передаємо це в C#. Ви можете поділитися цією змінною, клацнувши на "Variables" і вибравши "Share". Виберіть C#, і це додасть нову комірку, яка виглядатиме ось так (c# kernel):
#!set --value @pythonkernel:movies_json --name movies_json
Чудово! Тепер переглянемо змінну. Додайте нову комірку в C# kernel:
movies_json
Ок, час скористатися повною силою C# і LINQ для дослідження даних.
Створіть наступні комірки для дослідження та очищення даних.
(C# kernel)
using System.Text.Json;
using System;
var movies = movies_json.Deserialize<List<Dictionary<string, object>>>();
public record Movie
{
public string MOVIES { get; set; }
public string YEAR { get; set; }
public List<string> GENRE { get; set; }
public double? RATING { get; set; }
public string ONE_LINE { get; set; }
public List<string> Directors { get; set;}
public List<string> Actors { get; set;}
public int? VOTES { get; set; }
public string RunTime { get; set; }
public string Gross { get; set; }
}
var movie_records= (
from m in movies
let stars = m["STARS"].ToString()
let pipe_split = stars.Trim().Split("|")
let director_split = pipe_split.FirstOrDefault(p => p.Contains("Director"))
let star_split = pipe_split.FirstOrDefault(p => p.Contains("Star"))
let actor_part = star_split?.Split("\n").Where(x => !x.Contains("Star"))
.Select(x => x.Trim().Trim(',')).Where(x => !string.IsNullOrEmpty(x)).Distinct().OrderBy(x => x)
let actors = actor_part?.Any() != true ? null : actor_part.ToList()
let director_part = director_split?.Split("\n").Where(x => !x.Contains("Director"))
.Select(x => x.Trim().Trim(',')).Where(x => !string.IsNullOrEmpty(x)).Distinct().OrderBy(x => x)
let directors = director_part?.Any() != true ? null : director_part.ToList()
let genre = m["GENRE"].ToString().Trim()
let genre_split = string.IsNullOrEmpty(genre)
? null
: genre.Split(",").Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).Distinct().OrderBy(x => x)
let genres = genre_split?.Any() != true ? null : genre_split.ToList()
select new Movie {
GENRE = genres,
Gross = m["Gross"].ToString(),
MOVIES = m["MOVIES"].ToString(),
ONE_LINE = m["ONE-LINE"].ToString(),
RunTime = m["RunTime"].ToString(),
YEAR = m["YEAR"].ToString(),
RATING = double.TryParse(m["RATING"].ToString()?.Trim(),out var r) ? r : null,
VOTES = double.TryParse(m["VOTES"].ToString()?.Trim(),out var v) ? (int)v : null,
Actors = actors,
Directors = directors,
}).ToList();
// заповнити порожні місця null
(from m in movie_records
from p in m.GetType().GetProperties()
where p.PropertyType == typeof(string)
let v = ((string)p.GetValue(m))?.Trim()
let normalized_v = string.IsNullOrEmpty(v) ? null : v
select new {m,Prop = p, Value = normalized_v}
).ToList().ForEach(p => p.Prop.SetValue(p.m,p.Value));
У цих комірках.
я зробив:
- завантажив дані та відобразив їх у класі Movie
- заповнив порожні рядки значенням null, витягнув Directors і Actors із поля STARS у CSV
- перетворив RATING і VOTES, які були розпізнані як рядки, double або int, у відповідні типи.
Тепер ми можемо повністю досліджувати movie_records, використовуючи LINQ.
Подивимося, скільки у нас тут дублікатів.
// кількість дубльованих записів
(from m in movie_records
group m by m.MOVIES into g
where g.Count() > 1
from m in g.Take(g.Count() - 1)
select m
).Count()
Злиємо дублікати за допомогою LINQ.
// злиття дублікатів
movie_records =
(from m in movie_records
where m.MOVIES is not null
let name = m.MOVIES
//let director = string.Join("_",m.Directors.OrderBy(d => d).ToArray())
group m by new {name} into g
from m2 in g
let groupCount = g.Count()
let isDuplicate = groupCount > 1
let votes = g.Max(m => m.VOTES)
let rating = votes is null
? g.Min(m => m.RATING)
: g.FirstOrDefault(m => m.VOTES == votes)?.RATING
let genere = g.Where(m => m.GENRE != null).MaxBy(m => m.GENRE.Count)?.GENRE
let actors = g.Where(m => m.Actors != null).MaxBy(m => m.Actors.Count)?.Actors
let directors = g.Where(m => m.Directors != null).MaxBy(m => m.Directors.Count)?.Directors
let rec = !isDuplicate ? m2
: g.FirstOrDefault() with {
Actors = actors,
GENRE = genere,
Directors = directors,
RATING = rating,
VOTES = votes
}
where !isDuplicate || !g.Take(groupCount - 1).Any(x => ReferenceEqualityComparer.ReferenceEquals(x,m2))
select rec
).ToList();
І нарешті, давайте заповнимо null значення в RATING і VOTES за допомогою LINQ.
// заповнення null значень у рейтингах, обчислюючи середнє по групах
(from m in movie_records
where m.RATING is null
where m.Directors != null || m.GENRE != null
let directors = m.Directors
let generes = m.GENRE
let dirRatingAvg = directors is null ? null : movie_records
.Where(x => x.RATING != null && x.Directors != null && x.Directors.Intersect(directors).Any())
.Average(m => m.RATING)
let genreRatingAvg = generes is null ? null : movie_records
.Where(x => x.RATING != null && x.GENRE != null && x.GENRE.Intersect(generes).Any())
.Average(m => m.RATING)
let ratingAvg = dirRatingAvg ?? genreRatingAvg
where ratingAvg != null
select (m,ratingAvg)
).ToList().ForEach(x => x.m.RATING = x.ratingAvg);
// заповнення null значень у голосах, обчислюючи середнє по групах
(from m in movie_records
where m.VOTES is null
where m.Directors != null || m.GENRE != null
let directors = m.Directors
let generes = m.GENRE
let dirVoteAvg = directors is null ? null : movie_records
.Where(x => x.VOTES != null && x.Directors != null && x.Directors.Intersect(directors).Any())
.Average(m => m.VOTES)
let genreVoteAvg = generes is null ? null : movie_records
.Where(x => x.VOTES != null && x.GENRE != null && x.GENRE.Intersect(generes).Any())
.Average(m => m.VOTES)
let voteAvg = (int?)(dirVoteAvg ?? genreVoteAvg)
where voteAvg != null
select (m,voteAvg)
).ToList().ForEach(x => x.m.VOTES = x.voteAvg);
Поки що все добре.
нашими даними тепер можна працювати для візуалізації.
Для цього демо я використовую matplotlib та seaborn для візуалізації в Python і XPlot для візуалізації в C#.
Але спершу давайте поділимося новими очищеними movie_records з Python.
Додайте нову клітинку для Python або поділіться нею через VS Code, як обговорювалося раніше.
#!set --value @csharp:movie_records --name movie_records
Тепер імпортуємо matplotlib і seaborn і встановимо наш df на останні movie_records. (Python kernel)
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.DataFrame(movie_records)
Давайте відобразимо це, побудувавши точкову діаграму між MOVIES і RATING. (Python kernel)
plt.style.use('dark_background') # застосувати темну тему
plt.scatter(df["MOVIES"], df["RATING"], c='red', marker='o', edgecolors='white', s=100)
Або давайте побудуємо іншу точкову діаграму між RATING і VOTES, використовуючи C#.
Спершу давайте встановимо кілька NuGet пакетів. (C# kernel)
// matplotlib добре, але давайте спробуємо C# замість цього.
це шокуюче швидко!
#r "nuget: XPlot.Plotly"
#r "nuget: XPlot.Plotly.Interactive"
Давайте використаємо це для побудови графіка.
using XPlot.Plotly;
var chart = Chart.Plot(
new Scattergl() // видалити Graph.
{
x = movie_records.Select(x => x.VOTES),
y = movie_records.Select(x => x.RATING),
mode = "markers",
marker = new () // видалити Graph.
{
color = "red",
size = 10,
line = new () {color = "white",width=1}
}
}
);
chart.WithLayout(new()
{
title = "Scatter Plot Rating and Votes",
paper_bgcolor = "rgba(0, 0, 0, 0)",
plot_bgcolor = "rgba(0, 0, 0, 0)",
font = new() { color = "white" },
xaxis = new() { title = "Votes" },
yaxis = new() { title = "Rating" },
});
chart
Можливо, ви помітили, що графік на C# є інтерактивним і вимагає менше часу для рендерингу (в порівнянні з Python).
Ми можемо робити ще багато чого з цим, але я не буду цього охоплювати, дивіться мій ноутбук на GitHub.
Висновок:
Використовуючи Polyglot Notebook, ви можете безперешкодно поєднувати кілька мов і використовувати мову за вашим вибором. Діліться своїми нотатками та просто працюйте з AI/Data Science на мовах, окрім Python.
Будь ласка, діліться своїми думками в коментарях.
Перекладено з: .NET + Python + JS in Jupyter? Hello Polyglot Notebooks!