2025, Oct 04 07:16

Как обработать нечисловые runtimeMinutes в IMDB с pandas

Как исправить в pandas ошибку ValueError при чтении IMDB title.basics.tsv: выявляем нечисловые runtimeMinutes, добавляем их в na_values и загружаем как Int64.

Разбор файла IMDB title.basics.tsv с помощью pandas кажется простым, пока простое приведение типа не рушится с ошибкой ValueError: Unable to parse string "Reality-TV". Сбивает с толку то, что указанная в сообщении позиция может не совпадать с тем, что видно вокруг этой строки в исходном файле. Вместо охоты за номерами строк полезнее подтвердить, что действительно хранится в runtimeMinutes, и явно обработать нечисловые маркеры.

Воспроизводим ошибку

Типичный шаг загрузки — принудительно задать допускающий пропуски целочисленный тип через Int64 в pandas и считать "\N" отсутствующим значением. Ровно на этом месте и возникает сбой.

import pandas as pd

movie_data = pd.read_csv("title.basics.tsv",
                         sep="\t",
                         dtype={
                             "runtimeMinutes": "Int64",
                         },
                         na_values={
                             "runtimeMinutes": ["\\N"],
                         })

Исключение ValueError: Unable to parse string "Reality-TV" показывает, что в столбце встречаются значения, которые не являются числами и не покрыты текущим сопоставлением na_values.

Что на самом деле вызывает ошибку

Поле runtimeMinutes должно быть числовым, но на практике там встречаются и строковые значения. Эти текстовые токены нельзя привести к Int64 на этапе read_csv, отсюда и ошибка разбора. Практичный путь — перечислить уникальные значения, которые блокируют приведение, и пометить их как пропуски при загрузке.

Поиск нечисловых значений

Ниже фрагмент, который читает файл без принудительного dtype для runtimeMinutes, просматривает уникальные значения и собирает всё, что не проходит int(). Он также печатает проблемные значения, чтобы явно зафиксировать некорректные данные.

import pandas as pd

raw_frame = pd.read_csv("title.basics.tsv",
                        sep="\t",
                        na_values={
                            "runtimeMinutes": ["\\N"],
                        })

def extract_bad_markers(tbl, field_name):
    anomalies = []

    print(f"{'Type':20} | {'Value'}")
    print('-'*53)
    for item in tbl[field_name].unique():
        try:
            int(item)
        except:
            print(f"{str(type(item)):20} | {item}")
            anomalies.append(item)

    print("\nIncorrect values:", anomalies)
    return anomalies

invalid_values = extract_bad_markers(raw_frame, "runtimeMinutes")

Именно присутствие строк вроде "Reality-TV" в runtimeMinutes и вызывает ошибку парсинга.

Чистая загрузка: помечаем нечисловые значения как NA

Когда набор недопустимых маркеров известен, укажите read_csv трактовать их как пропуски вместе с "\N". Тогда pandas сможет безопасно загрузить столбец как Int64.

invalid_values.append("\\N")

clean_titles = pd.read_csv("title.basics.tsv",
                           sep="\t",
                           dtype={
                               "runtimeMinutes": "Int64",
                           },
                           na_values={
                               "runtimeMinutes": invalid_values,
                           })

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

Зачем это нужно в вашем конвейере данных

Предположения о схеме хрупки, когда в реальных наборах данных в одном поле смешиваются типы. Явно обнаружив и объявив все нечисловые токены как NA, вы делаете парсер детерминированным, сохраняете семантику nullable-целых и не гоняетесь за вводящими в заблуждение позициями из сообщений об ошибках. В результате получается воспроизводимый шаг загрузки, который падает реже и документирует особенности данных, которые придётся учитывать дальше по конвейеру.

Выводы

Если приведение dtype в pandas срывается, проверьте фактическую область значений столбца вместо того, чтобы полагаться на ожидания. Сначала считайте данные без dtype, перечислите уникальные значения и соберите всё, что не удаётся преобразовать через int(). Передайте этот набор в na_values и перезагрузите с целевым dtype. В случае IMDB title.basics.tsv это превращает runtimeMinutes в корректный столбец Int64, трактуя неожиданные строки, включая "\N", как пропуски. Сохраните очищенный датасет, чтобы в следующий раз пропустить этап обнаружения и держать конвейер быстрым и предсказуемым.

Материал основан на вопросе на StackOverflow от red_trumpet и ответе Sindik.