How concatenate `Audio` elements using batch mapping
Opened by bayartsogt-ya (closed)

### Describe the bug

I am trying to do concatenate audios in a dataset e.g. `google/fleurs`.
```python
print(dataset)
# Dataset({
#     features: ['path', 'audio'],
#     num_rows: 24
# })

def mapper_function(batch):
   # to merge every 3 audio
   # np.concatnate(audios[i: i+3]) for i in range(i, len(batch), 3)

dataset = dataset.map(mapper_function, batch=True, batch_size=24)
print(dataset)
# Expected output:
# Dataset({
#     features: ['path', 'audio'],
#     num_rows: 8
# })
```

I tried to construct `result={}` dictionary inside the mapper function, I just found it will not work because it needs `byte` also needed :((

I'd appreciate if your share any use cases similar to my problem or any solutions really. Thanks!

cc: @lhoestq 

### Steps to reproduce the bug

1. load audio dataset
2. try to merge every k audios and return as one

### Expected behavior

Merged dataset with a fewer rows. If we merge every 3 rows, then `n // 3` number of examples.

### Environment info

- `datasets` version: 2.1.0
- Platform: Linux-5.15.65+-x86_64-with-debian-bullseye-sid
- Python version: 3.7.12
- PyArrow version: 8.0.0
- Pandas version: 1.3.5

Comments:
You can try something like this ?
```python
def mapper_function(batch):
   return {"concatenated_audio": [np.concatenate([audio["array"] for audio in batch["audio"]])]}

dataset = dataset.map(
    mapper_function,
    batched=True,
    batch_size=3,
    remove_columns=list(dataset.features),
)
```

Thanks for the snippet!

One more question. I wonder why those two mappers are working so different that one taking 4 sec while other taking over 1 min :

```python
%%time
def mapper_function1(batch):
    # list_audio
    return {
        "audio": [
            {
                "array": np.concatenate([audio["array"] for audio in batch["audio"]]),
                "sampling_rate": 16_000,
            }
        ]
    }

dataset.map(
    mapper_function1,
    batched=True,
    batch_size=3,
    remove_columns=list(dataset.features),
)

# 100%
# 135/135 [01:13<00:00, 1.93ba/s]
# CPU times: user 1min 10s, sys: 3.21 s, total: 1min 13s
# Wall time: 1min 13s
# Dataset({
#     features: ['audio'],
#     num_rows: 135
# })

# --------------------------------
%%time
def mapper_function2(batch):
    # list_audio
    return {"audio": [np.concatenate([audio["array"] for audio in batch["audio"]])]}

dataset.map(
    mapper_function2,
    batched=True,
    batch_size=3,
    remove_columns=list(dataset.features),
)

# 100%
# 135/135 [00:03<00:00, 40.69ba/s]
# CPU times: user 1.88 s, sys: 1.48 s, total: 3.36 s
# Wall time: 4.8 s
# Dataset({
#     features: ['audio'],
#     num_rows: 135
# })
```


In the first one you get a dataset with an Audio type, and in the second one you get a dataset with a sequence of floats type.

The Audio type encodes the data as WAV to save disk space, so it takes more time to create.
The Audio type is automatically inferred because you modify the column "audio" which was already an Audio type. If you name it to something else, type inference will use a type struct with array and sampling rate fields.