Популярность Python в анализе данных и ML уже неоспорима, однако быстрорастущая звезда Rust готова бросить ему вызов. Python стал основным языком машинного обучения и анализа данных благодаря своей простоте, гибкости и огромному выбору вспомогательных библиотек. Процесс разработки на Python идет гораздо быстрее, чем на любом другом языке, и хотя Python довольно часто комбинируют с R и Julia, ни тот, ни другой язык не может полностью его заменить. Недавно у Python появился новый конкурент – Rust. Он гораздо сложнее Python, но у него есть два важных преимущества – высокая производительность, сопоставимая с C/C++, и максимально надежный механизм обеспечения безопасности. Теперь каждый ML-разработчик и аналитик данных должен решить для себя дилемму – какой из этих языков выбрать. Попробуем сравнить особенности Python, которые сделали его фактическим стандартом в AI/ML и анализе данных, с вескими преимуществами восходящей звезды Rust. ? Библиотека питониста Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» ?? Библиотека собеса по Python Подтянуть свои знания по Python вы можете на нашем телеграм-канале «Библиотека собеса по Python» ?? Библиотека задач по Python Интересные задачи по Python для практики можно найти на нашем телеграм-канале «Библиотека задач по Python» Наследие Python Python имеет простой и интуитивно понятный синтаксис, который иногда в шутку называют исполняемым псевдокодом. Эта простота и доступность приглянулись многим талантливым разработчикам: они начали создавать всевозможные дополнительные модули, библиотеки и фреймворки. В результате Python очень быстро обзавелся необъятной экосистемой, в которой есть инструменты для решения любых задач. Не все эти задачи решаются максимально эффективно – из-за невысокой производительности Python не подходит для разработки серьезных 3D-игр, например – но для анализа данных, машинного обучения и многих других вещей скорости языка вполне хватает. К тому же многие критически важные модули и библиотеки Python реализованы на уровне С и работают с соответствующей скоростью. Вот так просто выглядит чтение данных из CSV-файла в Python с помощью Pandas: import pandas as pd data = pd.read_csv("mydataset.csv") print(data.head()) Новый конкурент – Rust Основные плюсы Rust – высокая производительность, безопасность и многопоточность. Он отлично подходит для системного программирования, стремительно набирает популярность в серверной разработке и геймдеве. Rust – не самый очевидный выбор для анализа данных и машинного обучения. Однако в последние несколько лет, благодаря своим веским преимуществам, он все чаще применяется и в этих областях – хотя использовать его заметно сложнее. Чтение того же самого CSV-файла в Rust выглядит так: use std::error::Error; use csv::ReaderBuilder; fn main() -> Result<(), Box<dyn Error>> { let file = std::fs::File::open("mydataset.csv")?; let mut rdr = ReaderBuilder::new().has_headers(true).from_reader(file); for result in rdr.records() { let record = result?; println!("{:?}", record); } Ok(()) } Python или Rust: что проще Python известен плавной кривой обучения: с минимальными знаниями языка уже можно писать полезные скрипты, а изучение более сложных концепций отложить на потом. По этой причине Pyhton идеально подходит: Специалистам, которым нужно с помощью программирования решать профессиональные задачи – научные и инженерные. Новичкам, не имеющим никакого опыта в программировании. # Пример кода на Python print("Hello, World!") Rust, напротив, может показаться слишком сложным для начинающих: в нем есть непростые концепции, которые надо осмыслить сразу, например, системы владения и заимствования: Система владения в Rust означает, что каждый объект имеет только одного владельца, который отвечает за его уничтожение. Это предотвращает многие ошибки, связанные с утечкой памяти или двойным освобождением ресурсов. Система заимствования позволяет использовать один и тот же ресурс в нескольких местах кода без необходимости его копирования. Это может значительно повысить производительность, так как не тратятся ресурсы на создание лишних копий объектов. Однако эти концепции, среди прочих уникальных особенностей Rust, и делают его таким быстрым, безопасным и надежным. Это как раз тот случай, когда усилия, затраченные на изучение языка, будут многократно компенсированы качеством и производительностью готовых приложений. // Пример кода на Rust fn main() { println!("Hello, World!"); } Rust или Python: что быстрее Сравним производительность Rust и Python на решении одной и той же задачи. Напишем код, который вычисляет первые 35 чисел в последовательности Фибоначчи и выводит время, затраченное на это вычисление. Вычисление последовательности Фибоначчи на Rust: use std::time::Instant; fn fibonacci(n: u32) -> u32 { match n { 0 => 0, 1 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2), } } fn main() { let start_time = Instant::now(); for i in 0..35 { println!("{}", fibonacci(i)); } let duration = start_time.elapsed(); println!("Время выполнения: {:.2} секунд", duration.as_secs_f64()); } Результат: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 Время выполнения: 0.05 секунд Вариант на Python: import time def fibonacci(n): if n <= 0: return 0 elif n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2) start_time = time.time() for i in range(35): print(fibonacci(i)) end_time = time.time() print(f"Время выполнения : {end_time - start_time} секунд") Результат: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 Время выполнения : 7.8505754470825195 секунд Очевидно, что Rust справился с задачей быстрее:). Библиотеки и фреймворки С экосистемой Python сложно соперничать: библиотеки для математических вычислений, анализа данных и работы с нейронными сетями (Numpy, Pandas, TensorFlow, PyTorch, Scikit-Learn и т. д.) стали стандартом де-факто в индустрии анализа данных, Data Science и ML/AI. Однако Rust и его экосистема быстро развиваются: В распоряжении разработчиков есть модуль ndarray с аналогичной NumPy функциональностью. Имеется аналог Pandas для анализа данных – Polars. Есть библиотека statrs для статистических расчетов и анализа. Библиотека Tangram используется для машинного обучения и прогнозирования. Linfa, Autograd и SmartCore предоставляют функциональность, сходную с PyTorch и TensorFlow. Кроме того, PyTorch и TensorFlow тоже можно использовать в Rust, а список DS/ML/AI библиотек, разработанных специально для Rust, регулярно пополняется. ? Библиотека Data scientist’а Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека Data scientist’а» ?? Библиотека Data Science для собеса Подтянуть свои знания по DS вы можете на нашем телеграм-канале «Библиотека Data Science для собеса» ?? Библиотека задач по Data Science Интересные задачи по DS для практики можно найти на нашем телеграм-канале «Библиотека задач по Data Science» Безопасность и конкурентость В отличие от Python, Rust обеспечивает безопасность памяти: программа на Rust не может случайно обратиться к неверному адресу в памяти и вызвать ошибку. Это делает Rust более надежным и безопасным для использования в критических приложениях. Кроме того, Rust является языком с конкурентной моделью выполнения: несколько операций могут выполняться параллельно без необходимости блокировки или синхронизации. Это позволяет Rust достигать более высокой производительности, чем Python, особенно при работе с большими объемами данных. Для создания пула потоков и выполнения задач параллельно в Python можно использовать модуль concurrent.futures из стандартной библиотеки: import concurrent.futures def process_data(data_chunk): return data_chunk * 2 data_chunks = [1, 2, 3, 4, 5] with concurrent.futures.ThreadPoolExecutor() as executor: future_to_data = {executor.submit(process_data, data_chunk): data_chunk for data_chunk in data_chunks} for future in concurrent.futures.as_completed(future_to_data): data_chunk = future_to_data[future] try: data = future.result() except Exception as exc: print(f'{data_chunk} исключение: {exc}') else: print(f'{data_chunk} обработано: {data}') Для параллельных операций в Rust используется библиотека Rayon: use rayon::prelude::*; fn process_data(data_chunk: &mut Vec<i32>) { *data_chunk = data_chunk.iter().map(|&x| x * 2).collect(); } fn main() { let mut data_chunks: Vec<Vec<i32>> = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; data_chunks.par_iter_mut().for_each(|chunk| { process_data(chunk); }); println!("{:?}", data_chunks); } Управление памятью при обработке данных В Python управление памятью абстрагировано, что делает его более простым для разработчиков и специалистов по анализу данных, поскольку они могут сосредоточиться на алгоритмах, а не на тонкостях использования памяти. Однако это может привести к проблемам при работе с большими объемами информации: Python использует автоматическую сборку мусора, которая может быть неэффективной для объемных наборов данных. В Rust есть возможность управлять памятью на более низком уровне, обеспечивая эффективное использование ресурсов. Это позволяет точно контролировать использование памяти и может быть очень полезно при работе с большими наборами данных. В приведенном ниже примере происходит следующее: large_array создается внутри функции main и становится владельцем Vec. Это означает, что large_array отвечает за освобождение памяти, выделенной для Vec, когда он выходит из области видимости. sum создается как ссылка на результат вызова large_array.iter().sum(). Таким образом, sum не владеет данными, а просто ссылается на них. В конце функции main, когда large_array выходит из области видимости, память, выделенная для Vec, автоматически освобождается. Это гарантируется системой управления памятью Rust, которая автоматически освобождает память, когда владелец выходит из области видимости. fn main() { let mut large_array: Vec<f64> = vec![1.0; 1_000_000]; let sum: f64 = large_array.iter().sum(); println!("Сумма элементов равна {}", sum); } Параллелизм и многопоточность В Python многопоточность и параллелизм можно реализовать помощью модулей threading и multiprocessing, но они имеют свои особенности и ограничения. В частности, из-за Global Interpreter Lock (GIL) в Python, многопоточность не всегда может привести к увеличению производительности при выполнении задач, которые интенсивно нагружают процессор. В то же время модуль multiprocessing позволяет обойти GIL, создавая отдельные процессы, каждый из которых имеет свой собственный интерпретатор Python и свою собственную копию памяти. Приведенный ниже пример демонстрирует многопроцессорность, которая является формой параллелизма: import multiprocessing import time def heavy(data, i, proc): for index in range(len(data)): data[index] += 1 print(f"Обработка № {i} ядро {proc}") def sequential(calc, proc, data): print(f"Запускаем поток № {proc}") for i in range(calc): heavy(data, i, proc) print(f"{calc} обработок данных завершено. Процессор № {proc}") def processesed(procs, calc): # procs - количество ядер # calc - количество операций на ядро processes = [] manager = multiprocessing.Manager() data = manager.list([num for num in range(1, 10)]) # делим вычисления на количество ядер for proc in range(procs): p = multiprocessing.Process(target=sequential, args=(calc, proc, data)) processes.append(p) p.start() # ждем, пока все ядра завершат свою работу for p in processes: p.join() return data start = time.time() # узнаем количество ядер у процессора n_proc = multiprocessing.cpu_count() # вычисляем, сколько циклов вычислений будет приходиться # на 1 ядро, чтобы в сумме получилось 80+ calc = 80 // n_proc + 1 data = processesed(n_proc, calc) end = time.time() print(f"Количество ядер в процессоре: {n_proc}n" f"На каждом ядре произведено обработок данных: {calc}n" f"Итого {n_proc * calc} обработок за: {end - start}n" f"Обработанные данные: {data}") В Rust есть библиотека Rayon, которая позволяет легко преобразовать последовательные вычисления в параллельные. Rayon гарантирует отсутствие условий гонки данных, что идеально обеспечивает параллельность вычислений: use rayon::prelude::*; use num_cpus; fn main() { let mut data: Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; // Определяем количество ядер процессора let num_cpus = num_cpus::get(); // Вычисляем количество циклов вычислений для каждого ядра let num_cycles = 80 / num_cpus + 1; // Выполняем циклы вычислений for _ in 0..num_cycles { data.par_iter_mut().for_each(|x| { *x += 1; }); } println!("{:?}", data); } Визуализация данных Python располагает несколькими библиотеками для визуализации данных: Мatplotlib, Seaborn, Bokeh, Plotly, Altair и т.д. Самые популярные из них – Мatplotlib и Seaborn. Эти библиотеки позволяют легко создавать графики и диаграммы, что делает визуализацию данных в Python максимально простой и удобной: import matplotlib.pyplot as plt # Данные data = [1, 2, 3, 4, 5] # Названия месяцев months = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май'] # Создаем график fig, ax = plt.subplots() # Выводим данные ax.plot(months, data) # Устанавливаем названия осей ax.set_xlabel('Месяц') ax.set_ylabel('Данные') plt.show() Результат: В Rust для визуализации можно использовать библиотеку plotters: use plotters::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let data = vec![1, 2, 3, 4, 5]; let months = vec!["Январь", "Февраль", "Март", "Апрель", "Май"]; let root = BitMapBackend::new("plot.png", (640, 480)).into_drawing_area(); root.fill(&WHITE)?; let mut chart = ChartBuilder::on(&root) .caption("Месячная статистика", ("Arial", 50).into_font()) .margin(5) .x_label_area_size(30) .y_label_area_size(30) .build_cartesian_2d(months.iter().cloned().map(|m| m.to_string()).collect::<Vec<String>>(), 0..10)?; chart.configure_mesh().draw()?; chart.draw_series(LineSeries::new( data.iter().enumerate().map(|(i, &v)| (months[i].to_string(), v)), &BLUE, ))?; Ok(()) } Интеграция с другими языками программирования Python может бесшовно интегрироваться с библиотеками C и C++ с помощью Cython. Это дает возможность использовать функции и данные из этих библиотек в Python-коде. Если у вас есть библиотека my_lib на C: // my_lib.c #include <stdlib.h> int add(int a, int b) { return a + b; } Ее можно скомпилировать: gcc -shared -o my_lib.so my_lib.c И вызвать в Python: from ctypes import CDLL # Загрузка библиотеки my_lib = CDLL('./my_lib.so') # Вызов функции add result = my_lib.add(1, 2) print(result) # Выводит: 3 Rust предлагает возможности для интеграции с библиотеками C через Foreign Function Interface (FFI). FFI позволяет вызывать функции, определенные в другом языке программирования, из кода на Rust. Это достигается путем определения внешней функции в Rust с сигнатурой функции, совместимой с C, и затем динамического связывания с общей библиотекой, содержащей реализацию функции: // Пример использования FFI для вызова функции C из Rust extern "C" { fn my_c_function(arg1: i32, arg2: f64) -> f64; } fn main() { let result = unsafe { my_c_function(42, 3.14) }; println!("Result: {}", result); } Стоит заметить, в Python-проектах можно использовать модули, написанные на Rust, и наоборот, в Rust можно вызывать Python. Проще всего это сделать с помощью фреймворка PyO3, который позволяет создавать нативные модули Python на Rust. PyO3 обеспечивает простоту и удобство в создании привязок и интеграции кода Rust и Python. Вот простейший пример создания Python-модуля на Rust: // Rust код, использующий PyO3 для создания модуля Python use pyo3::prelude::*; use pyo3::wrap_pyfunction; #[pyfunction] fn process(data: Vec<i32>) -> Vec<i32> { data.iter().map(|x| x * 2).collect() } #[pymodule] fn rust_module(py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(process, m)?)?; Ok(()) } Скомпилированный модуль в коде Python можно использовать так: import rust_module data = [1, 2, 3, 4, 5] result = rust_module.process(data) print(result) # Выводит: [2, 4, 6, 8, 10] Подведем итоги Выбор между Python и Rust для анализа данных и машинного обучения – сложная дилемма, поскольку оба языка предлагают уникальные преимущества: Python обладает максимально простым, понятным и гибким синтаксисом, располагает обширной экосистемой библиотек и фреймворков для машинного обучения и работы с данными. Это делает его отличным выбором для разработчиков, которые только начинают изучать анализ данных и машинное обучение. С другой стороны, Rust обеспечивает максимальную производительность, безопасность, эффективную поддержку многопоточности и параллелизма. Rust отлично подходит для продвинутых разработчиков, создающих ПО для работы с большими наборами данных и выполнения сложных вычислительных задач. В конечном итоге выбор между Python и Rust зависит от ваших конкретных потребностей и уровня опыта. Если вы только начинаете изучать анализ данных и машинное обучение, то лучше выбрать Python. Если у вас уже есть опыт работы с другими языками программирования, а ваш проект нуждается в более высокой производительности и безопасности, то Rust будет самым подходящим вариантом. *** Материалы по теме ? Машинное обучение: что это такое и как оно работает ? Создание собственного ИИ-бота на Python за 33 строчки кода
Python стал основным языком машинного обучения и анализа данных благодаря своей простоте, гибкости и огромному выбору вспомогательных библиотек. Процесс разработки на Python идет гораздо быстрее, чем на любом другом языке, и хотя Python довольно часто комбинируют с R и Julia, ни тот, ни другой язык не может полностью его заменить. Недавно у Python появился новый конкурент – Rust. Он гораздо сложнее Python, но у него есть два важных преимущества – высокая производительность, сопоставимая с C/C++, и максимально надежный механизм обеспечения безопасности. Теперь каждый ML-разработчик и аналитик данных должен решить для себя дилемму – какой из этих языков выбрать. Попробуем сравнить особенности Python, которые сделали его фактическим стандартом в AI/ML и анализе данных, с вескими преимуществами восходящей звезды Rust.
? Библиотека питониста Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» ?? Библиотека собеса по Python Подтянуть свои знания по Python вы можете на нашем телеграм-канале «Библиотека собеса по Python» ?? Библиотека задач по Python Интересные задачи по Python для практики можно найти на нашем телеграм-канале «Библиотека задач по Python»
Python имеет простой и интуитивно понятный синтаксис, который иногда в шутку называют исполняемым псевдокодом. Эта простота и доступность приглянулись многим талантливым разработчикам: они начали создавать всевозможные дополнительные модули, библиотеки и фреймворки. В результате Python очень быстро обзавелся необъятной экосистемой, в которой есть инструменты для решения любых задач. Не все эти задачи решаются максимально эффективно – из-за невысокой производительности Python не подходит для разработки серьезных 3D-игр, например – но для анализа данных, машинного обучения и многих других вещей скорости языка вполне хватает. К тому же многие критически важные модули и библиотеки Python реализованы на уровне С и работают с соответствующей скоростью. Вот так просто выглядит чтение данных из CSV-файла в Python с помощью Pandas:
import pandas as pd data = pd.read_csv("mydataset.csv") print(data.head())
Основные плюсы Rust – высокая производительность, безопасность и многопоточность. Он отлично подходит для системного программирования, стремительно набирает популярность в серверной разработке и геймдеве. Rust – не самый очевидный выбор для анализа данных и машинного обучения. Однако в последние несколько лет, благодаря своим веским преимуществам, он все чаще применяется и в этих областях – хотя использовать его заметно сложнее. Чтение того же самого CSV-файла в Rust выглядит так:
use std::error::Error; use csv::ReaderBuilder; fn main() -> Result<(), Box<dyn Error>> { let file = std::fs::File::open("mydataset.csv")?; let mut rdr = ReaderBuilder::new().has_headers(true).from_reader(file); for result in rdr.records() { let record = result?; println!("{:?}", record); } Ok(()) }
Python известен плавной кривой обучения: с минимальными знаниями языка уже можно писать полезные скрипты, а изучение более сложных концепций отложить на потом. По этой причине Pyhton идеально подходит:
# Пример кода на Python print("Hello, World!")
Rust, напротив, может показаться слишком сложным для начинающих: в нем есть непростые концепции, которые надо осмыслить сразу, например, системы владения и заимствования:
Однако эти концепции, среди прочих уникальных особенностей Rust, и делают его таким быстрым, безопасным и надежным. Это как раз тот случай, когда усилия, затраченные на изучение языка, будут многократно компенсированы качеством и производительностью готовых приложений.
// Пример кода на Rust fn main() { println!("Hello, World!"); }
Сравним производительность Rust и Python на решении одной и той же задачи. Напишем код, который вычисляет первые 35 чисел в последовательности Фибоначчи и выводит время, затраченное на это вычисление.
Вычисление последовательности Фибоначчи на Rust:
use std::time::Instant; fn fibonacci(n: u32) -> u32 { match n { 0 => 0, 1 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2), } } fn main() { let start_time = Instant::now(); for i in 0..35 { println!("{}", fibonacci(i)); } let duration = start_time.elapsed(); println!("Время выполнения: {:.2} секунд", duration.as_secs_f64()); }
Результат:
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 Время выполнения: 0.05 секунд
Вариант на Python:
import time def fibonacci(n): if n <= 0: return 0 elif n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2) start_time = time.time() for i in range(35): print(fibonacci(i)) end_time = time.time() print(f"Время выполнения : {end_time - start_time} секунд")
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 Время выполнения : 7.8505754470825195 секунд
Очевидно, что Rust справился с задачей быстрее:).
С экосистемой Python сложно соперничать: библиотеки для математических вычислений, анализа данных и работы с нейронными сетями (Numpy, Pandas, TensorFlow, PyTorch, Scikit-Learn и т. д.) стали стандартом де-факто в индустрии анализа данных, Data Science и ML/AI.
Однако Rust и его экосистема быстро развиваются:
Кроме того, PyTorch и TensorFlow тоже можно использовать в Rust, а список DS/ML/AI библиотек, разработанных специально для Rust, регулярно пополняется.
? Библиотека Data scientist’а Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека Data scientist’а» ?? Библиотека Data Science для собеса Подтянуть свои знания по DS вы можете на нашем телеграм-канале «Библиотека Data Science для собеса» ?? Библиотека задач по Data Science Интересные задачи по DS для практики можно найти на нашем телеграм-канале «Библиотека задач по Data Science»
В отличие от Python, Rust обеспечивает безопасность памяти: программа на Rust не может случайно обратиться к неверному адресу в памяти и вызвать ошибку. Это делает Rust более надежным и безопасным для использования в критических приложениях.
Кроме того, Rust является языком с конкурентной моделью выполнения: несколько операций могут выполняться параллельно без необходимости блокировки или синхронизации. Это позволяет Rust достигать более высокой производительности, чем Python, особенно при работе с большими объемами данных.
Для создания пула потоков и выполнения задач параллельно в Python можно использовать модуль concurrent.futures из стандартной библиотеки:
import concurrent.futures def process_data(data_chunk): return data_chunk * 2 data_chunks = [1, 2, 3, 4, 5] with concurrent.futures.ThreadPoolExecutor() as executor: future_to_data = {executor.submit(process_data, data_chunk): data_chunk for data_chunk in data_chunks} for future in concurrent.futures.as_completed(future_to_data): data_chunk = future_to_data[future] try: data = future.result() except Exception as exc: print(f'{data_chunk} исключение: {exc}') else: print(f'{data_chunk} обработано: {data}')
Для параллельных операций в Rust используется библиотека Rayon:
use rayon::prelude::*; fn process_data(data_chunk: &mut Vec<i32>) { *data_chunk = data_chunk.iter().map(|&x| x * 2).collect(); } fn main() { let mut data_chunks: Vec<Vec<i32>> = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; data_chunks.par_iter_mut().for_each(|chunk| { process_data(chunk); }); println!("{:?}", data_chunks); }
В Python управление памятью абстрагировано, что делает его более простым для разработчиков и специалистов по анализу данных, поскольку они могут сосредоточиться на алгоритмах, а не на тонкостях использования памяти. Однако это может привести к проблемам при работе с большими объемами информации: Python использует автоматическую сборку мусора, которая может быть неэффективной для объемных наборов данных.
В Rust есть возможность управлять памятью на более низком уровне, обеспечивая эффективное использование ресурсов. Это позволяет точно контролировать использование памяти и может быть очень полезно при работе с большими наборами данных. В приведенном ниже примере происходит следующее:
fn main() { let mut large_array: Vec<f64> = vec![1.0; 1_000_000]; let sum: f64 = large_array.iter().sum(); println!("Сумма элементов равна {}", sum); }
В Python многопоточность и параллелизм можно реализовать помощью модулей threading и multiprocessing, но они имеют свои особенности и ограничения. В частности, из-за Global Interpreter Lock (GIL) в Python, многопоточность не всегда может привести к увеличению производительности при выполнении задач, которые интенсивно нагружают процессор. В то же время модуль multiprocessing позволяет обойти GIL, создавая отдельные процессы, каждый из которых имеет свой собственный интерпретатор Python и свою собственную копию памяти. Приведенный ниже пример демонстрирует многопроцессорность, которая является формой параллелизма:
import multiprocessing import time def heavy(data, i, proc): for index in range(len(data)): data[index] += 1 print(f"Обработка № {i} ядро {proc}") def sequential(calc, proc, data): print(f"Запускаем поток № {proc}") for i in range(calc): heavy(data, i, proc) print(f"{calc} обработок данных завершено. Процессор № {proc}") def processesed(procs, calc): # procs - количество ядер # calc - количество операций на ядро processes = [] manager = multiprocessing.Manager() data = manager.list([num for num in range(1, 10)]) # делим вычисления на количество ядер for proc in range(procs): p = multiprocessing.Process(target=sequential, args=(calc, proc, data)) processes.append(p) p.start() # ждем, пока все ядра завершат свою работу for p in processes: p.join() return data start = time.time() # узнаем количество ядер у процессора n_proc = multiprocessing.cpu_count() # вычисляем, сколько циклов вычислений будет приходиться # на 1 ядро, чтобы в сумме получилось 80+ calc = 80 // n_proc + 1 data = processesed(n_proc, calc) end = time.time() print(f"Количество ядер в процессоре: {n_proc}n" f"На каждом ядре произведено обработок данных: {calc}n" f"Итого {n_proc * calc} обработок за: {end - start}n" f"Обработанные данные: {data}")
В Rust есть библиотека Rayon, которая позволяет легко преобразовать последовательные вычисления в параллельные. Rayon гарантирует отсутствие условий гонки данных, что идеально обеспечивает параллельность вычислений:
use rayon::prelude::*; use num_cpus; fn main() { let mut data: Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; // Определяем количество ядер процессора let num_cpus = num_cpus::get(); // Вычисляем количество циклов вычислений для каждого ядра let num_cycles = 80 / num_cpus + 1; // Выполняем циклы вычислений for _ in 0..num_cycles { data.par_iter_mut().for_each(|x| { *x += 1; }); } println!("{:?}", data); }
Python располагает несколькими библиотеками для визуализации данных: Мatplotlib, Seaborn, Bokeh, Plotly, Altair и т.д. Самые популярные из них – Мatplotlib и Seaborn. Эти библиотеки позволяют легко создавать графики и диаграммы, что делает визуализацию данных в Python максимально простой и удобной:
import matplotlib.pyplot as plt # Данные data = [1, 2, 3, 4, 5] # Названия месяцев months = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май'] # Создаем график fig, ax = plt.subplots() # Выводим данные ax.plot(months, data) # Устанавливаем названия осей ax.set_xlabel('Месяц') ax.set_ylabel('Данные') plt.show()
В Rust для визуализации можно использовать библиотеку plotters:
use plotters::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let data = vec![1, 2, 3, 4, 5]; let months = vec!["Январь", "Февраль", "Март", "Апрель", "Май"]; let root = BitMapBackend::new("plot.png", (640, 480)).into_drawing_area(); root.fill(&WHITE)?; let mut chart = ChartBuilder::on(&root) .caption("Месячная статистика", ("Arial", 50).into_font()) .margin(5) .x_label_area_size(30) .y_label_area_size(30) .build_cartesian_2d(months.iter().cloned().map(|m| m.to_string()).collect::<Vec<String>>(), 0..10)?; chart.configure_mesh().draw()?; chart.draw_series(LineSeries::new( data.iter().enumerate().map(|(i, &v)| (months[i].to_string(), v)), &BLUE, ))?; Ok(()) }
Python может бесшовно интегрироваться с библиотеками C и C++ с помощью Cython. Это дает возможность использовать функции и данные из этих библиотек в Python-коде.
Если у вас есть библиотека my_lib на C:
// my_lib.c #include <stdlib.h> int add(int a, int b) { return a + b; }
Ее можно скомпилировать:
gcc -shared -o my_lib.so my_lib.c
И вызвать в Python:
from ctypes import CDLL # Загрузка библиотеки my_lib = CDLL('./my_lib.so') # Вызов функции add result = my_lib.add(1, 2) print(result) # Выводит: 3
Rust предлагает возможности для интеграции с библиотеками C через Foreign Function Interface (FFI). FFI позволяет вызывать функции, определенные в другом языке программирования, из кода на Rust. Это достигается путем определения внешней функции в Rust с сигнатурой функции, совместимой с C, и затем динамического связывания с общей библиотекой, содержащей реализацию функции:
// Пример использования FFI для вызова функции C из Rust extern "C" { fn my_c_function(arg1: i32, arg2: f64) -> f64; } fn main() { let result = unsafe { my_c_function(42, 3.14) }; println!("Result: {}", result); }
Стоит заметить, в Python-проектах можно использовать модули, написанные на Rust, и наоборот, в Rust можно вызывать Python. Проще всего это сделать с помощью фреймворка PyO3, который позволяет создавать нативные модули Python на Rust. PyO3 обеспечивает простоту и удобство в создании привязок и интеграции кода Rust и Python. Вот простейший пример создания Python-модуля на Rust:
// Rust код, использующий PyO3 для создания модуля Python use pyo3::prelude::*; use pyo3::wrap_pyfunction; #[pyfunction] fn process(data: Vec<i32>) -> Vec<i32> { data.iter().map(|x| x * 2).collect() } #[pymodule] fn rust_module(py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(process, m)?)?; Ok(()) }
Скомпилированный модуль в коде Python можно использовать так:
import rust_module data = [1, 2, 3, 4, 5] result = rust_module.process(data) print(result) # Выводит: [2, 4, 6, 8, 10]
Выбор между Python и Rust для анализа данных и машинного обучения – сложная дилемма, поскольку оба языка предлагают уникальные преимущества:
В конечном итоге выбор между Python и Rust зависит от ваших конкретных потребностей и уровня опыта. Если вы только начинаете изучать анализ данных и машинное обучение, то лучше выбрать Python. Если у вас уже есть опыт работы с другими языками программирования, а ваш проект нуждается в более высокой производительности и безопасности, то Rust будет самым подходящим вариантом.
***
Ваш адрес email не будет опубликован. Обязательные поля помечены *
Сохранить моё имя, email и адрес сайта в этом браузере для последующих моих комментариев.
Δ
Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.