# Процесс

🤗 Datasets предоставляет множество инструментов для изменения структуры и содержимого набора данных. Эти инструменты важны для наведения порядка в наборе данных, создания дополнительных столбцов, преобразования между признаками и форматами, а также для многих других задач.

В этом руководстве показано, как:

- Переставьте строки и разделите набор данных.
- Переименование и удаление столбцов, а также другие общие операции со столбцами.
- Примените функции обработки к каждому примеру в наборе данных.
- Соедините наборы данных.
- Применить пользовательское преобразование форматирования.
- Сохранение и экспорт обработанных наборов данных.

Для получения подробной информации о обработке других модальностей данных обратитесь к руководству по обработке аудиоданных, руководству по обработке изображений или руководству по обработке текстовых данных.

Примеры в этом руководстве используют набор данных MRPC, но вы можете загрузить любой другой набор данных по вашему выбору и следовать инструкциям!

```py
>>> from datasets import load_dataset
>>> dataset = load_dataset("nyu-mll/glue", "mrpc", split="train")
```

> [!WARNING]
> Все методы обработки в этом руководстве возвращают новый объект [Dataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset). Изменения не выполняются на месте. Будьте осторожны, чтобы не перезаписать предыдущий набор данных!

## Сортировка, перемешивание, выбор, разбиение и шардирование

Существует несколько функций для изменения структуры набора данных. Эти функции полезны для выбора только нужных строк, создания обучающей и тестовой выборок, а также для разбиения очень больших наборов данных на более мелкие фрагменты.

### Сортировка

Используйте [sort()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.sort) для сортировки значений столбца по их числовым значениям. Предоставленный столбец должен быть совместим с NumPy.

```py
>>> dataset["label"][:10]
[1, 0, 1, 0, 1, 1, 0, 1, 0, 0]
>>> sorted_dataset = dataset.sort("label")
>>> sorted_dataset["label"][:10]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> sorted_dataset["label"][-10:]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
```

Под капотом это создаёт список индексов, отсортированный по значениям столбца. Затем это отображение индексов используется для доступа к нужным строкам в базовой таблице Arrow.

### Перемешивание

Функция [shuffle()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.shuffle) случайно переставляет значения в столбце. Вы можете указать параметр `generator` в этой функции, чтобы использовать другой `numpy.random.Generator`, если вам требуется больший контроль над алгоритмом, используемым для перемешивания набора данных.

```py
>>> shuffled_dataset = sorted_dataset.shuffle(seed=42)
>>> shuffled_dataset["label"][:10]
[1, 1, 1, 0, 1, 1, 1, 1, 1, 0]
```

Shuffling берёт список индексов `[0:len(my_dataset)]` и перемешивает его, чтобы создать отображение индексов. Однако как только ваш [Dataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset) получает отображение индексов, скорость может снизиться в 10 раз. Это происходит из-за дополнительного шага получения индекса строки для чтения с использованием отображения индексов, и, что наиболее важно, вы больше не читаете непрерывные блоки данных. Чтобы восстановить скорость, вам нужно будет переписать весь набор данных на диске с помощью [Dataset.flatten_indices()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.flatten_indices), что удаляет отображение индексов. Альтернативно, вы можете перейти на [IterableDataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.IterableDataset) и использовать его быстрое приближённое перемешивание [IterableDataset.shuffle()](/docs/datasets/main/ru/package_reference/main_classes#datasets.IterableDataset.shuffle):

```py
>>> iterable_dataset = dataset.to_iterable_dataset(num_shards=128)
>>> shuffled_iterable_dataset = iterable_dataset.shuffle(seed=42, buffer_size=1000)
```

### Выбор и фильтрация

Существует два способа фильтрации строк в наборе данных: [select()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.select) и [filter()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.filter).

- [select()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.select) возвращает строки по списку индексов:

```py
>>> small_dataset = dataset.select([0, 10, 20, 30, 40, 50])
>>> len(small_dataset)
6
```

- [filter()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.filter) возвращает строки, соответствующие заданному условию:

```py
>>> start_with_ar = dataset.filter(lambda example: example["sentence1"].startswith("Ar"))
>>> len(start_with_ar)
6
>>> start_with_ar["sentence1"]
['Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A $ 4.56 , having earlier set a record high of A $ 4.57 .',
'Arison said Mann may have been one of the pioneers of the world music movement and he had a deep love of Brazilian music .',
'Arts helped coach the youth on an eighth-grade football team at Lombardi Middle School in Green Bay .',
'Around 9 : 00 a.m. EDT ( 1300 GMT ) , the euro was at $ 1.1566 against the dollar , up 0.07 percent on the day .',
"Arguing that the case was an isolated example , Canada has threatened a trade backlash if Tokyo 's ban is not justified on scientific grounds .",
'Artists are worried the plan would harm those who need help most - performers who have a difficult time lining up shows .'
]
```

[filter()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.filter) также может фильтровать по индексам, если установить `with_indices=True`:

```py
>>> even_dataset = dataset.filter(lambda example, idx: idx % 2 == 0, with_indices=True)
>>> len(even_dataset)
1834
>>> len(dataset) / 2
1834.0
```

Если список индексов для сохранения не является непрерывным, эти методы также создают отображение индексов под капотом.

### Разделение

Функция [train_test_split()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.train_test_split) создаёт обучающую и тестовую выборки, если в вашем наборе данных их ещё нет. Это позволяет настраивать относительные пропорции или абсолютное количество образцов в каждой выборке. В примере ниже используйте параметр `test_size`, чтобы создать тестовую выборку, составляющую 10% от исходного набора данных:

```py
>>> dataset.train_test_split(test_size=0.1)
{'train': Dataset(schema: {'sentence1': 'string', 'sentence2': 'string', 'label': 'int64', 'idx': 'int32'}, num_rows: 3301),
'test': Dataset(schema: {'sentence1': 'string', 'sentence2': 'string', 'label': 'int64', 'idx': 'int32'}, num_rows: 367)}
>>> 0.1 * len(dataset)
366.8
```

Разметки перемешиваются по умолчанию, но вы можете установить `shuffle=False`, чтобы отключить перемешивание.

### Шард

🤗 Datasets поддерживает шардирование для разделения очень большого набора данных на заранее определённое количество фрагментов. Укажите параметр `num_shards` в [shard()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.shard), чтобы определить, на сколько шардов разбить набор данных. Также необходимо указать с помощью параметра `index`, какой именно шард нужно вернуть.

Например, набор данных [stanfordnlp/imdb](https://huggingface.co/datasets/stanfordnlp/imdb) содержит 25000 примеров:

```py
>>> from datasets import load_dataset
>>> dataset = load_dataset("stanfordnlp/imdb", split="train")
>>> print(dataset)
Dataset({
    features: ['text', 'label'],
    num_rows: 25000
})
```

После разбиения набора данных на четыре фрагмента в первом фрагменте будет только 6250 примеров:

```py
>>> dataset.shard(num_shards=4, index=0)
Dataset({
    features: ['text', 'label'],
    num_rows: 6250
})
>>> print(25000/4)
6250.0
```

## Переименование, удаление, приведение типов и разворачивание

Следующие функции позволяют изменять столбцы набора данных. Эти функции полезны для переименования или удаления столбцов, замены столбцов на новый набор признаков, а также для разворачивания вложенных структур столбцов.

### Переименовать

Используйте [rename_column()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.rename_column), когда нужно переименовать столбец в наборе данных. Признаки, связанные с исходным столбцом, фактически переносятся под новым именем столбца, а не просто заменяют исходный столбец на месте.

Передайте в [rename_column()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.rename_column) имя исходной колонки и новое имя колонки:

```py
>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'label', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.rename_column("sentence1", "sentenceA")
>>> dataset = dataset.rename_column("sentence2", "sentenceB")
>>> dataset
Dataset({
    features: ['sentenceA', 'sentenceB', 'label', 'idx'],
    num_rows: 3668
})
```

### Удалить

Если нужно удалить одну или несколько колонок, передайте имя удаляемой колонки в функцию [remove_columns()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.remove_columns). Чтобы удалить несколько колонок, передайте список имён колонок:

```py
>>> dataset = dataset.remove_columns("label")
>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.remove_columns(["sentence1", "sentence2"])
>>> dataset
Dataset({
    features: ['idx'],
    num_rows: 3668
})
```

Напротив, [select_columns()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.select_columns) выбирает одну или несколько столбцов для сохранения и удаляет остальные. Эта функция принимает одно имя столбца или список имён:

```py
>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'label', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.select_columns(['sentence1', 'sentence2', 'idx'])
>>> dataset
Dataset({
    features: ['sentence1', 'sentence2', 'idx'],
    num_rows: 3668
})
>>> dataset = dataset.select_columns('idx')
>>> dataset
Dataset({
    features: ['idx'],
    num_rows: 3668
})
```

### Приведение

Функция [cast()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.cast) изменяет тип признаков одной или нескольких колонок. В качестве аргумента эта функция принимает ваши новые [Features](/docs/datasets/main/ru/package_reference/main_classes#datasets.Features). Пример ниже показывает, как изменить признаки [ClassLabel](/docs/datasets/main/ru/package_reference/main_classes#datasets.ClassLabel) и [Value](/docs/datasets/main/ru/package_reference/main_classes#datasets.Value):

```py
>>> dataset.features
{'sentence1': Value('string'),
'sentence2': Value('string'),
'label': ClassLabel(names=['not_equivalent', 'equivalent']),
'idx': Value('int32')}

>>> from datasets import ClassLabel, Value
>>> new_features = dataset.features.copy()
>>> new_features["label"] = ClassLabel(names=["negative", "positive"])
>>> new_features["idx"] = Value("int64")
>>> dataset = dataset.cast(new_features)
>>> dataset.features
{'sentence1': Value('string'),
'sentence2': Value('string'),
'label': ClassLabel(names=['negative', 'positive']),
'idx': Value('int64')}
```

> [!TIP]
> Преобразование типов работает только в том случае, если исходный и новый типы признаков совместимы. Например, столбец с типом признака `Value("int32")` можно преобразовать в `Value("bool")`, если исходный столбец содержит только единицы и нули.

Используйте функцию [cast_column()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.cast_column), чтобы изменить тип признака в одном столбце. Передайте имя столбца и его новый тип признака в качестве аргументов:

```py
>>> dataset.features
{'audio': Audio(sampling_rate=44100, mono=True)}

>>> dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))
>>> dataset.features
{'audio': Audio(sampling_rate=16000, mono=True)}
```

### Свернуть

Иногда столбец может представлять собой вложенную структуру нескольких типов. Обратите внимание на следующую вложенную структуру из набора данных SQuAD:

```py
>>> from datasets import load_dataset
>>> dataset = load_dataset("rajpurkar/squad", split="train")
>>> dataset.features
{'id': Value('string'),
 'title': Value('string'),
 'context': Value('string'),
 'question': Value('string'),
 'answers': {'text': List(Value('string')),
  'answer_start': List(Value('int32'))}}
```

Поле `answers` содержит два подполя: `text` и `answer_start`. Используйте функцию [flatten()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.flatten), чтобы извлечь подполя в отдельные столбцы:

```py
>>> flat_dataset = dataset.flatten()
>>> flat_dataset
Dataset({
    features: ['id', 'title', 'context', 'question', 'answers.text', 'answers.answer_start'],
 num_rows: 87599
})
```

Обратите внимание, что подполя теперь представляют собой собственные независимые столбцы: `answers.text` и `answers.answer_start`.

## Карта

Некоторые из наиболее мощных применений 🤗 Datasets связаны с использованием функции [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map). Основная цель [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) — ускорить обработку данных. Она позволяет применять функцию обработки к каждому примеру в наборе данных, независимо или партиями. Эта функция даже может создавать новые строки и столбцы.

В следующем примере добавьте префикс `'My sentence: '` к каждому значению `sentence1` в наборе данных.

Начните с создания функции, которая добавляет `'My sentence: '` в начало каждого предложения. Функция должна принимать и возвращать `dict`:

```py
>>> def add_prefix(example):
...     example["sentence1"] = 'My sentence: ' + example["sentence1"]
...     return example
```

Теперь используйте [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map), чтобы применить функцию `add_prefix` ко всему набору данных:

```py
>>> updated_dataset = small_dataset.map(add_prefix)
>>> updated_dataset["sentence1"][:5]
['My sentence: Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
"My sentence: Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .",
'My sentence: They had published an advertisement on the Internet on June 10 , offering the cargo for sale , he added .',
'My sentence: Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A $ 4.56 , having earlier set a record high of A $ 4.57 .',
]
```

Рассмотрим ещё один пример, но на этот раз вы удалите столбец с помощью [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map). При удалении столбца он удаляется только после того, как пример был передан в отображаемую функцию. Это позволяет отображаемой функции использовать содержимое столбцов до их удаления.

Укажите столбец для удаления с помощью параметра `remove_columns` в [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map):

```py
>>> updated_dataset = dataset.map(lambda example: {"new_sentence": example["sentence1"]}, remove_columns=["sentence1"])
>>> updated_dataset.column_names
['sentence2', 'label', 'idx', 'new_sentence']
```

> [!TIP]
> 🤗 В Datasets также есть функция [remove_columns()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.remove_columns), которая работает быстрее, поскольку не копирует данные оставшихся столбцов.

Вы также можете использовать [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) с индексами, если установите `with_indices=True`. В примере ниже индекс добавляется в начало каждого предложения:

```py
>>> updated_dataset = dataset.map(lambda example, idx: {"sentence2": f"{idx}: " + example["sentence2"]}, with_indices=True)
>>> updated_dataset["sentence2"][:5]
['0: Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
 "1: Yucaipa bought Dominick 's in 1995 for $ 693 million and sold it to Safeway for $ 1.8 billion in 1998 .",
 "2: On June 10 , the ship 's owners had published an advertisement on the Internet , offering the explosives for sale .",
 '3: Tab shares jumped 20 cents , or 4.6 % , to set a record closing high at A $ 4.57 .',
 '4: PG & E Corp. shares jumped $ 1.63 or 8 percent to $ 21.03 on the New York Stock Exchange on Friday .'
]
```

### Многопроцессность

Многопоточность значительно ускоряет обработку за счёт параллельного выполнения процессов на CPU. Установите параметр `num_proc` в [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map), чтобы задать количество процессов:

```py
>>> updated_dataset = dataset.map(lambda example, idx: {"sentence2": f"{idx}: " + example["sentence2"]}, with_indices=True, num_proc=4)
```

Метод [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) также работает с рангом процесса, если установить `with_rank=True`. Это аналогично параметру `with_indices`. Параметр `with_rank` в отображаемой функции располагается после параметра `index`, если тот уже присутствует.

```py
>>> import torch
>>> from multiprocess import set_start_method
>>> from transformers import AutoTokenizer, AutoModelForCausalLM
>>> from datasets import load_dataset
>>>
>>> # Get an example dataset
>>> dataset = load_dataset("fka/awesome-chatgpt-prompts", split="train")
>>>
>>> # Get an example model and its tokenizer
>>> model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen1.5-0.5B-Chat").eval()
>>> tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B-Chat")
>>>
>>> def gpu_computation(batch, rank):
...     # Move the model on the right GPU if it's not there already
...     device = f"cuda:{(rank or 0) % torch.cuda.device_count()}"
...     model.to(device)
...
...     # Your big GPU call goes here, for example:
...     chats = [[
...         {"role": "system", "content": "You are a helpful assistant."},
...         {"role": "user", "content": prompt}
...     ] for prompt in batch["prompt"]]
...     texts = [tokenizer.apply_chat_template(
...         chat,
...         tokenize=False,
...         add_generation_prompt=True
...     ) for chat in chats]
...     model_inputs = tokenizer(texts, padding=True, return_tensors="pt").to(device)
...     with torch.no_grad():
...         outputs = model.generate(**model_inputs, max_new_tokens=512)
...     batch["output"] = tokenizer.batch_decode(outputs, skip_special_tokens=True)
...     return batch
>>>
>>> if __name__ == "__main__":
...     set_start_method("spawn")
...     updated_dataset = dataset.map(
...         gpu_computation,
...         batched=True,
...         batch_size=16,
...         with_rank=True,
...         num_proc=torch.cuda.device_count(),  # one process per GPU
...     )
```

Основное применение rank — параллелизация вычислений на нескольких GPU. Для этого необходимо установить `multiprocess.set_start_method("spawn")`. В противном случае вы получите следующую ошибку CUDA:

```bash
RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the 'spawn' start method.
```

### Пакетная обработка

Функция [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) поддерживает работу с батчами примеров. Чтобы оперировать батчами, установите `batched=True`. Размер батча по умолчанию составляет 1000, но его можно изменить с помощью параметра `batch_size`. Обработка батчами позволяет реализовать интересные приложения, такие как разбиение длинных предложений на более короткие фрагменты и аугментация данных.

#### Разделение длинных примеров

Если примеры слишком длинные, их можно разбить на несколько более мелких фрагментов. Начните с создания функции, которая:

1. Разбивает поле `sentence1` на фрагменты по 50 символов.

2. Собирает все фрагменты воедино, чтобы создать новый набор данных.

```py
>>> def chunk_examples(examples):
...     chunks = []
...     for sentence in examples["sentence1"]:
...         chunks += [sentence[i:i + 50] for i in range(0, len(sentence), 50)]
...     return {"chunks": chunks}
```

Примените функцию с помощью [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map):

```py
>>> chunked_dataset = dataset.map(chunk_examples, batched=True, remove_columns=dataset.column_names)
>>> chunked_dataset[:10]
{'chunks': ['Amrozi accused his brother , whom he called " the ',
            'witness " , of deliberately distorting his evidenc',
            'e .',
            "Yucaipa owned Dominick 's before selling the chain",
            ' to Safeway in 1998 for $ 2.5 billion .',
            'They had published an advertisement on the Interne',
            't on June 10 , offering the cargo for sale , he ad',
            'ded .',
            'Around 0335 GMT , Tab shares were up 19 cents , or',
            ' 4.4 % , at A $ 4.56 , having earlier set a record']}
```

Обратите внимание, что теперь предложения разбиты на более короткие фрагменты, а в наборе данных стало больше строк.

```py
>>> dataset
Dataset({
 features: ['sentence1', 'sentence2', 'label', 'idx'],
 num_rows: 3668
})
>>> chunked_dataset
Dataset({
    features: ['chunks'],
    num_rows: 10470
})
```

#### Аугментация данных

Функция [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) также может использоваться для аугментации данных. В следующем примере генерируются дополнительные слова для замаскированного токена в предложении.

Загрузите и используйте модель [RoBERTa](https://huggingface.co/roberta-base) в [FillMaskPipeline](https://huggingface.co/transformers/main_classes/pipelines#transformers.FillMaskPipeline) библиотеки 🤗 Transformers:

```py
>>> from random import randint
>>> from transformers import pipeline

>>> fillmask = pipeline("fill-mask", model="roberta-base")
>>> mask_token = fillmask.tokenizer.mask_token
>>> smaller_dataset = dataset.filter(lambda e, i: i<100, with_indices=True)
```

Создайте функцию для случайного выбора слова, которое нужно замаскировать в предложении. Функция также должна возвращать исходное предложение и две лучшие замены, сгенерированные RoBERTA.

```py
>>> def augment_data(examples):
...     outputs = []
...     for sentence in examples["sentence1"]:
...         words = sentence.split(' ')
...         K = randint(1, len(words)-1)
...         masked_sentence = " ".join(words[:K]  + [mask_token] + words[K+1:])
...         predictions = fillmask(masked_sentence)
...         augmented_sequences = [predictions[i]["sequence"] for i in range(3)]
...         outputs += [sentence] + augmented_sequences
...
...     return {"data": outputs}
```

Используйте [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map), чтобы применить функцию ко всему набору данных:

```py
>>> augmented_dataset = smaller_dataset.map(augment_data, batched=True, remove_columns=dataset.column_names, batch_size=8)
>>> augmented_dataset[:9]["data"]
['Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'Amrozi accused his brother, whom he called " the witness ", of deliberately withholding his evidence.',
 'Amrozi accused his brother, whom he called " the witness ", of deliberately suppressing his evidence.',
 'Amrozi accused his brother, whom he called " the witness ", of deliberately destroying his evidence.',
 "Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .",
 'Yucaipa owned Dominick Stores before selling the chain to Safeway in 1998 for $ 2.5 billion.',
 "Yucaipa owned Dominick's before selling the chain to Safeway in 1998 for $ 2.5 billion.",
 'Yucaipa owned Dominick Pizza before selling the chain to Safeway in 1998 for $ 2.5 billion.'
]
```

Для каждого исходного предложения RoBERTa дополняла случайное слово тремя альтернативами. Исходное слово `distorting` дополняется словами `withholding`, `suppressing` и `destroying`.

### Асинхронная обработка

Асинхронные функции полезны для параллельного вызова конечных точек API, например, для загрузки контента, такого как изображения, или для обращения к конечной точке модели.

Вы можете определить асинхронную функцию с помощью ключевых слов `async` и `await`. Ниже приведён пример функции для вызова чат-модели из Hugging Face:

```python
>>> import aiohttp
>>> import asyncio
>>> from huggingface_hub import get_token
>>> sem = asyncio.Semaphore(20)  # max number of simultaneous queries
>>> async def query_model(model, prompt):
...     api_url = f"https://api-inference.huggingface.co/models/{model}/v1/chat/completions"
...     headers = {"Authorization": f"Bearer {get_token()}", "Content-Type": "application/json"}
...     json = {"messages": [{"role": "user", "content": prompt}], "max_tokens": 20, "seed": 42}
...     async with sem, aiohttp.ClientSession() as session, session.post(api_url, headers=headers, json=json) as response:
...         output = await response.json()
...         return {"Output": output["choices"][0]["message"]["content"]}
```

Асинхронные функции выполняются параллельно, что значительно ускоряет процесс. Тот же код займёт гораздо больше времени при последовательном выполнении, так как он простаивает, ожидая ответа модели. Как правило, рекомендуется использовать `async` / `await`, если функция должна ждать ответа от API, например, или если она загружает данные, на что может уйти некоторое время.

Обратите внимание на наличие `Semaphore`: он задает максимальное количество запросов, которые могут выполняться параллельно. Рекомендуется использовать `Semaphore` при вызове API, чтобы избежать ошибок, связанных с лимитами частоты запросов.

Используем его для вызова модели [microsoft/Phi-3-mini-4k-instruct](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct) и попросим её вернуть основную тему каждой математической задачи из набора данных [Maxwell-Jia/AIME_2024](https://huggingface.co/Maxwell-Jia/AIME_2024):

````python
>>> from datasets import load_dataset
>>> ds = load_dataset("Maxwell-Jia/AIME_2024", split="train")
>>> model = "microsoft/Phi-3-mini-4k-instruct"
>>> prompt = 'What is this text mainly about ? Here is the text:\n\n```\n{Problem}\n```\n\nReply using one or two words max, e.g. "The main topic is Linear Algebra".'
>>> async def get_topic(example):
...     return await query_model(model, prompt.format(Problem=example['Problem']))
>>> ds = ds.map(get_topic)
>>> ds[0]
{'ID': '2024-II-4',
 'Problem': 'Let $x,y$ and $z$ be positive real numbers that...',
 'Solution': 'Denote $\\log_2(x) = a$, $\\log_2(y) = b$, and...,
 'Answer': 33,
 'Output': 'The main topic is Logarithms.'}
````

Здесь [Dataset.map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) выполняет множество функций `get_topic` асинхронно, поэтому не приходится ждать каждый ответ модели, что заняло бы много времени при последовательном выполнении.

По умолчанию [Dataset.map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) запускает до одной тысячи функций map параллельно, поэтому не забудьте установить максимальное количество API-вызовов, которые могут выполняться параллельно, с помощью `Semaphore`, иначе модель может возвращать ошибки ограничения частоты запросов или перегружаться. Для продвинутых сценариев использования вы можете изменить максимальное количество параллельных запросов в `datasets.config`.

### Обработка нескольких разбиений

Многие наборы данных содержат разбиения, которые можно обрабатывать одновременно с помощью [DatasetDict.map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.DatasetDict.map). Например, чтобы токенизировать поле `sentence1` в разбиениях train и test, выполните:

```py
>>> from datasets import load_dataset

# load all the splits
>>> dataset = load_dataset('nyu-mll/glue', 'mrpc')
>>> encoded_dataset = dataset.map(lambda examples: tokenizer(examples["sentence1"]), batched=True)
>>> encoded_dataset["train"][0]
{'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
'label': 1,
'idx': 0,
'input_ids': [  101,  7277,  2180,  5303,  4806,  1117,  1711,   117,  2292, 1119,  1270,   107,  1103,  7737,   107,   117,  1104,  9938, 4267, 12223, 21811,  1117,  2554,   119,   102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
```

### Использование в распределённых системах

При использовании [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) в распределённой среде также следует применять [torch.distributed.barrier](https://pytorch.org/docs/stable/distributed?highlight=barrier#torch.distributed.barrier). Это гарантирует, что отображение выполняет основной процесс, а остальные процессы загружают результаты, что позволяет избежать дублирования работы.

В следующем примере показано, как можно использовать `torch.distributed.barrier` для синхронизации процессов:

```py
>>> from datasets import Dataset
>>> import torch.distributed

>>> dataset1 = Dataset.from_dict({"a": [0, 1, 2]})

>>> if training_args.local_rank > 0:
...     print("Waiting for main process to perform the mapping")
...     torch.distributed.barrier()

>>> dataset2 = dataset1.map(lambda x: {"a": x["a"] + 1})

>>> if training_args.local_rank == 0:
...     print("Loading results from main process")
...     torch.distributed.barrier()
```

## Пакет

Метод `batch()` позволяет группировать образцы из набора данных в батчи. Это особенно полезно, когда нужно создавать батчи данных для обучения или оценки, особенно при работе с моделями глубокого обучения.

Вот пример использования метода `batch()`:

```python
>>> from datasets import load_dataset
>>> dataset = load_dataset("cornell-movie-review-data/rotten_tomatoes", split="train")
>>> batched_dataset = dataset.batch(batch_size=4)
>>> batched_dataset[0]
{'text': ['the rock is destined to be the 21st century\'s new " conan " and that he\'s going to make a splash even greater than arnold schwarzenegger , jean-claud van damme or steven segal .',
        'the gorgeously elaborate continuation of " the lord of the rings " trilogy is so huge that a column of words cannot adequately describe co-writer/director peter jackson\'s expanded vision of j . r . r . tolkien\'s middle-earth .',
        'effective but too-tepid biopic',
        'if you sometimes like to go to the movies to have fun , wasabi is a good place to start .'],
'label': [1, 1, 1, 1]}
```

Метод `batch()` принимает следующие параметры:

- `batch_size` (`int`): количество образцов в каждой партии.
- `drop_last_batch` (`bool`, по умолчанию `False`): определяет, следует ли отбрасывать последний неполный батч, если размер набора данных не делится на размер батча.
- `num_proc` (`int`, необязательный параметр, по умолчанию `None`): количество процессов для использования при многопроцессной обработке. Если значение равно None, многопроцессная обработка не используется. Это может значительно ускорить формирование пакетов для больших наборов данных.

Обратите внимание, что `Dataset.batch()` возвращает новый [Dataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset), в котором каждый элемент представляет собой батч из нескольких образцов исходного набора данных. Если вы хотите обрабатывать данные батчами, следует использовать батчированный [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) напрямую: он применяет функцию к батчам, но возвращаемый набор данных не является батчированным.

## Соединить

Раздельные наборы данных можно объединить, если у них совпадают типы столбцов. Объедините наборы данных с помощью [concatenate_datasets()](/docs/datasets/main/ru/package_reference/main_classes#datasets.concatenate_datasets):

```py
>>> from datasets import concatenate_datasets, load_dataset

>>> stories = load_dataset("ajibawa-2023/General-Stories-Collection", split="train")
>>> stories = stories.select_columns(["text"])  # only keep the 'text' column
>>> wiki = load_dataset("wikimedia/wikipedia", "20231101.en", split="train")
>>> wiki = wiki.select_columns(["text"])  # only keep the 'text' column

>>> assert stories.features.type == wiki.features.type
>>> bert_dataset = concatenate_datasets([stories, wiki])
```

Также можно объединить два набора данных по горизонтали, установив `axis=1`, при условии, что у наборов одинаковое количество строк:

```py
>>> from datasets import Dataset
>>> stories_ids = Dataset.from_dict({"ids": list(range(len(stories)))})
>>> stories_with_ids = concatenate_datasets([stories, stories_ids], axis=1)
```

### Чередование

Также можно объединить несколько наборов данных, чередуя примеры из каждого из них, чтобы создать новый набор данных. Этот процесс называется _interleaving_ и выполняется с помощью функции [interleave_datasets()](/docs/datasets/main/ru/package_reference/main_classes#datasets.interleave_datasets). Обе функции — [interleave_datasets()](/docs/datasets/main/ru/package_reference/main_classes#datasets.interleave_datasets) и [concatenate_datasets()](/docs/datasets/main/ru/package_reference/main_classes#datasets.concatenate_datasets) — работают с обычными объектами [Dataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset) и [IterableDataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.IterableDataset). Пример того, как чередовать объекты [IterableDataset](/docs/datasets/main/ru/package_reference/main_classes#datasets.IterableDataset), приведён в руководстве по [Stream](./stream#interleave).

Вы можете определить вероятности выборки для каждого из исходных наборов данных, чтобы указать, как чередовать их. В этом случае новый набор данных формируется путём последовательного извлечения примеров из случайного набора данных, пока один из наборов не исчерпает все свои примеры.

```py
>>> from datasets import Dataset, interleave_datasets
>>> seed = 42
>>> probabilities = [0.3, 0.5, 0.2]
>>> d1 = Dataset.from_dict({"a": [0, 1, 2]})
>>> d2 = Dataset.from_dict({"a": [10, 11, 12, 13]})
>>> d3 = Dataset.from_dict({"a": [20, 21, 22]})
>>> dataset = interleave_datasets([d1, d2, d3], probabilities=probabilities, seed=seed)
>>> dataset["a"]
[10, 11, 20, 12, 0, 21, 13]
```

Вы также можете указать `stopping_strategy`. Стратегия по умолчанию, `first_exhausted`, является стратегией подвыборки, то есть построение набора данных останавливается, как только один из наборов исчерпывает свои примеры. Вы можете указать `stopping_strategy=all_exhausted`, чтобы применить стратегию перебора с повторами. В этом случае построение набора данных останавливается, как только каждый пример из каждого набора был добавлен хотя бы один раз. На практике это означает, что если набор данных исчерпан, он возвращается к началу этого набора, пока не будет достигнут критерий остановки. Обратите внимание, что если вероятности выборки не указаны, новый набор данных будет содержать `max_length_datasets*nb_dataset samples`. Также существует `stopping_strategy=all_exhausted_without_replacement`, чтобы гарантировать, что каждый пример будет просмотрен ровно один раз.

```py
>>> d1 = Dataset.from_dict({"a": [0, 1, 2]})
>>> d2 = Dataset.from_dict({"a": [10, 11, 12, 13]})
>>> d3 = Dataset.from_dict({"a": [20, 21, 22]})
>>> dataset = interleave_datasets([d1, d2, d3], stopping_strategy="all_exhausted")
>>> dataset["a"]
[0, 10, 20, 1, 11, 21, 2, 12, 22, 0, 13, 20]
```

## Формат

Функция [with_format()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.with_format) изменяет формат столбца, чтобы сделать его совместимым с некоторыми распространёнными форматами данных. Укажите желаемый результат в параметре `type`. Также можно выбрать столбцы, которые нужно отформатировать, с помощью `columns=`. Форматирование применяется на лету.

Например, создайте тензоры PyTorch, установив `type="torch"`:

```py
>>> dataset = dataset.with_format(type="torch")
```

Функция [set_format()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.set_format) также изменяет формат столбца, но делает это на месте:

```py
>>> dataset.set_format(type="torch")
```

Если вам нужно вернуть набор данных к исходному формату, установите формат в `None` (или используйте [reset_format()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.reset_format)):

```py
>>> dataset.format
{'type': 'torch', 'format_kwargs': {}, 'columns': [...], 'output_all_columns': False}
>>> dataset = dataset.with_format(None)
>>> dataset.format
{'type': None, 'format_kwargs': {}, 'columns': [...], 'output_all_columns': False}
```

### Форматы тензоров

Поддерживается несколько форматов тензоров или массивов. Как правило, рекомендуется использовать эти форматы вместо ручного преобразования выходов набора данных в тензоры или массивы, чтобы избежать ненужного копирования данных и ускорить загрузку.

Вот список поддерживаемых форматов тензоров или массивов:

- NumPy: имя формата — "numpy", подробная информация в разделе [Использование Datasets с NumPy](use_with_numpy)
- PyTorch: имя формата — "torch", подробнее см. [Использование наборов данных с PyTorch](use_with_pytorch)
- TensorFlow: имя формата — "tensorflow", подробная информация в разделе [Использование наборов данных с TensorFlow](use_with_tensorflow)
- JAX: имя формата — "jax", для получения дополнительной информации см. [Использование Datasets с JAX](use_with_jax)

> [!TIP]
> См. руководство [Использование Datasets с TensorFlow](use_with_tensorflow#using-totfdataset) для получения подробной информации о том, как эффективно создать набор данных TensorFlow.

Когда набор данных имеет формат тензора или массива, все данные представлены в виде тензоров или массивов (за исключением неподдерживаемых типов, например, строк для PyTorch):

```python
>>> ds = Dataset.from_dict({"text": ["foo", "bar"], "tokens": [[0, 1, 2], [3, 4, 5]]})
>>> ds = ds.with_format("torch")
>>> ds[0]
{'text': 'foo', 'tokens': tensor([0, 1, 2])}
>>> ds[:2]
{'text': ['foo', 'bar'],
 'tokens': tensor([[0, 1, 2],
         [3, 4, 5]])}
```

### Табличные форматы

Вы можете использовать формат dataframes или таблиц для оптимизации загрузки и обработки данных, поскольку они, как правило, обеспечивают операции без копирования и трансформации, написанные на низкоуровневых языках.

Вот список поддерживаемых форматов dataframes или таблиц:

- Pandas: имя формата — "pandas", подробнее см. [Использование наборов данных с Pandas](use_with_pandas)
- Polars: имя формата — "polars", подробная информация в разделе [Использование Datasets с Polars](use_with_polars)
- PyArrow: имя формата — "arrow", подробнее см. [Использование Datasets с PyArrow](use_with_tensorflow)

Когда набор данных имеет формат dataframe или таблицы, каждая строка набора данных или пачки строк форматируются как dataframe или таблица, а столбцы набора данных — как series или массив:

```python
>>> ds = Dataset.from_dict({"text": ["foo", "bar"], "label": [0, 1]})
>>> ds = ds.with_format("pandas")
>>> ds[:2]
  text  label
0  foo      0
1  bar      1
```

Эти форматы позволяют быстрее работать с данными, избегая их копирования, а также обеспечивают более быструю обработку данных в [map()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.map) или [filter()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.filter):

```python
>>> ds = ds.map(lambda df: df.assign(upper_text=df.text.str.upper()), batched=True)
>>> ds[:2]
  text  label upper_text
0  foo      0        FOO
1  bar      1        BAR
```

### Преобразование пользовательского формата

Функция [with_transform()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.with_transform) применяет пользовательское преобразование форматирования на лету. Эта функция заменяет любой ранее указанный формат. Например, с помощью этой функции можно токенизировать и дополнять токены на лету. Токенизация применяется только при обращении к примерам:

```py
>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
>>> def encode(batch):
...     return tokenizer(batch["sentence1"], batch["sentence2"], padding="longest", truncation=True, max_length=512, return_tensors="pt")
>>> dataset = dataset.with_transform(encode)
>>> dataset.format
{'type': 'custom', 'format_kwargs': {'transform': <function __main__.encode(batch)>}, 'columns': ['idx', 'label', 'sentence1', 'sentence2'], 'output_all_columns': False}
```

Также существует [set_transform()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.set_transform), который выполняет то же самое, но изменяет данные на месте.

Вы также можете использовать функцию [with_transform()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.with_transform) для пользовательского декодирования в [Features](/docs/datasets/main/ru/package_reference/main_classes#datasets.Features).

Пример ниже использует пакет [`pydub`](http://pydub.com/) в качестве альтернативы декодированию с помощью `torchcodec`:

```py
>>> import numpy as np
>>> from pydub import AudioSegment

>>> audio_dataset_amr = Dataset.from_dict({"audio": ["audio_samples/audio.amr"]})

>>> def decode_audio_with_pydub(batch, sampling_rate=16_000):
...     def pydub_decode_file(audio_path):
...         sound = AudioSegment.from_file(audio_path)
...         if sound.frame_rate != sampling_rate:
...             sound = sound.set_frame_rate(sampling_rate)
...         channel_sounds = sound.split_to_mono()
...         samples = [s.get_array_of_samples() for s in channel_sounds]
...         fp_arr = np.array(samples).T.astype(np.float32)
...         fp_arr /= np.iinfo(samples[0].typecode).max
...         return fp_arr
...
...     batch["audio"] = [pydub_decode_file(audio_path) for audio_path in batch["audio"]]
...     return batch

>>> audio_dataset_amr.set_transform(decode_audio_with_pydub)
```

## Сохранить

Когда ваш набор данных будет готов, вы можете сохранить его в формате Parquet как Hugging Face Dataset и использовать его позже с помощью [load_dataset()](/docs/datasets/main/ru/package_reference/loading_methods#datasets.load_dataset).

Сохраните набор данных, указав имя репозитория набора данных на Hugging Face, в который вы хотите его сохранить, в [push_to_hub()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.push_to_hub):

```python
encoded_dataset.push_to_hub("username/my_dataset")
```

Вы можете использовать несколько процессов для параллельной загрузки. Это особенно полезно, если вы хотите ускорить процесс:

```python
dataset.push_to_hub("username/my_dataset", num_proc=8)
```

Используйте функцию [load_dataset()](/docs/datasets/main/ru/package_reference/loading_methods#datasets.load_dataset) для повторной загрузки набора данных (в режиме потоковой обработки или без него):

```python
from datasets import load_dataset
reloaded_dataset = load_dataset("username/my_dataset", streaming=True)
```

В качестве альтернативы вы можете сохранить его локально на диске в формате Arrow. В отличие от Parquet, Arrow не сжимается, что значительно ускоряет повторную загрузку, что идеально подходит для локального использования на диске и временного кэширования. Однако из-за большего размера и меньшего количества метаданных загрузка, выгрузка и запросы к данным в формате Arrow выполняются медленнее, чем в Parquet, а также он менее подходит для долгосрочного хранения.

Use the [save_to_disk()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.save_to_disk) and [load_from_disk()](/docs/datasets/main/ru/package_reference/loading_methods#datasets.load_from_disk) function to reload the dataset from your disk:

```py
>>> encoded_dataset.save_to_disk("path/of/my/dataset/directory")
>>> # later
>>> from datasets import load_from_disk
>>> reloaded_dataset = load_from_disk("path/of/my/dataset/directory")
```

## Экспорт

🤗 Datasets поддерживает экспорт, что позволяет работать с вашим набором данных в других приложениях. В таблице ниже перечислены поддерживаемые на данный момент форматы файлов для экспорта:

| Тип файла               | Метод экспорта                                                       |
| ----------------------- | ------------------------------------------------------------------- |
| CSV                     | [Dataset.to_csv()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.to_csv)                                                  |
| JSON                    | [Dataset.to_json()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.to_json)                                                 |
| Parquet                 | [Dataset.to_parquet()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.to_parquet)                                              |
| SQL                     | [Dataset.to_sql()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.to_sql)                                                  |
| Объект Python в памяти | [Dataset.to_pandas()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.to_pandas), `Dataset.to_polars()` or [Dataset.to_dict()](/docs/datasets/main/ru/package_reference/main_classes#datasets.Dataset.to_dict) |

Например, экспортируйте ваш набор данных в файл CSV следующим образом:

```py
>>> encoded_dataset.to_csv("path/of/my/dataset.csv")
```

Используйте путь `hf://`, чтобы экспортировать в [репозиторий Dataset](https://huggingface.co/docs/hub/datasets-overview) или [Storage Bucket](https://huggingface.co/docs/hub/storage-buckets) на Hugging Face:

```py
>>> encoded_dataset.to_csv("hf://datasets/username/dataset_name/path/of/my/dataset.csv")
>>> encoded_dataset.to_csv("hf://buckets/username/raw_data_bucket/path/of/my/dataset.csv")
```

