2025, Oct 01 03:17
Как разделить столбец в Pandas по последней запятой с помощью str.rsplit
Почему apply вызывает ValueError в Pandas при делении строки на столбцы, и как исправить это через str.rsplit(',', n=1): делим по последней запятой корректно.
Разбивать один столбец Pandas на два кажется обманчиво простым — пока безобидное присваивание не выдает ValueError: Cannot set a DataFrame with multiple columns to the single column. Ниже — короткий разбор причины сбоя и надежный способ делить по последней запятой, даже когда в тексте их несколько.
Проблема
Есть функция, которая принимает датафрейм из шести столбцов и пытается разделить текстовый столбец на два новых с помощью построчного apply. Внутри функции это падает с ошибкой несоответствия формы; снаружи — нет. Идея проста: найти запятую и взять левую/правую части.
def build_frame():
    df_six = "some data"
    def left_of_comma(txt):
        ln = len(txt)
        rng = range(ln)
        sep = ","
        for i in rng:
            if txt[i] == sep:
                pos = i
        return txt[0:pos]
    def right_of_comma(txt):
        ln = len(txt)
        rng = range(ln)
        sep = ","
        for i in rng:
            if txt[i] == sep:
                pos = i
        return txt[pos+1:]
    df_seven["Column Val1"] = df_six.apply(lambda row: left_of_comma(row["Splittable column"]), axis=1)
    df_eight["Column Val2"] = df_seven.apply(lambda row: right_of_comma(row["Splittable column"]), axis=1)
    df_out = base_df_6c.drop("Splittable Colum", axis=1)
    return df_out
Что на самом деле идет не так
Ошибка Pandas означает, что правая часть присваивания оказалась DataFrame, а не одномерной Series. В итоге попытка записать это в один столбец и вызывает исключение. Опора на построчный apply и ручной перебор строки делает операцию хрупкой. Проще и надежнее — векторизованный подход: он полностью исключает несоответствие форм и корректно обрабатывает строки с несколькими запятыми.
Решение: делим по последней запятой с помощью str.rsplit
Если возможны несколько запятых, используйте правое разделение с n=1 — так строка разделится только по последней запятой. Первая часть становится объяснением, вторая — статусом. Если после запятой есть пробелы, их стоит удалить.
df["Column Val1"] = df["Splittable column"].str.rsplit(",", n=1).str[0]
df["Column Val2"] = df["Splittable column"].str.rsplit(",", n=1).str[1]
Если нужно обрезать пробелы:
parts = df["Splittable column"].str.rsplit(",", n=1)
df["Column Val1"] = parts.str[0].str.strip()
df["Column Val2"] = parts.str[1].str.strip()
Можно сразу создать оба столбца с expand=True, а затем убрать пробелы:
df[["Column Val1", "Column Val2"]] = (
    df["Splittable column"].str.rsplit(",", n=1, expand=True)
      .apply(lambda s: s.str.strip())
)
Полный пример
В этом наборе данных в некоторых строках несколько запятых; требуется, чтобы последний фрагмент стал статусом, а все, что перед ним, — причиной.
import pandas as pd
sample = {
    "Index": ["1", "2", "3", "4"],
    "Splittable column": [
        ",FALSE",
        "Not Acceptable,Failed,NAN,FALSE",
        "Not acceptable,FALSE",
        ",FALSE",
    ],
}
df_in = pd.DataFrame(sample)
# Вариант 1: цепочка .str-аксессоров
df_in["Column Val1"] = df_in["Splittable column"].str.rsplit(",", n=1).str[0].str.strip()
df_in["Column Val2"] = df_in["Splittable column"].str.rsplit(",", n=1).str[1].str.strip()
# Вариант 2: развернуть сразу в два столбца
# df_in[["Column Val1", "Column Val2"]] = (
#     df_in["Splittable column"].str.rsplit(",", n=1, expand=True)
#           .apply(lambda s: s.str.strip())
# )
# При необходимости удалите исходный текстовый столбец
# df_in.drop(columns="Splittable column", inplace=True)
print(df_in)
Так последний токен, например FALSE, остается в Column Val2, а вся левая часть, включая промежуточные запятые, сохраняется в Column Val1.
Почему это важно
Массовое разделение строк — частая задача, и с apply легко получить объект неправильной размерности. Векторизованные строковые методы, такие как str.rsplit с n=1, напрямую выражают намерение, единообразно обрабатывают строки с несколькими запятыми и устраняют сюрпризы при присваивании. Добавление str.strip() убирает лишние пробелы, которые часто остаются после разделителей.
Выводы
Отдавайте предпочтение векторизованным строковым операциям вместо построчного apply при разбиении столбцов. Делите по последней запятой через str.rsplit(',', n=1), чтобы корректно обрабатывать входные данные с несколькими запятыми, и используйте str.strip(), если после разделителя могут быть пробелы. Если столкнулись с несоответствием формы, сначала проверьте правую часть присваивания: присвойте ее во временную переменную и выведите тип и форму. Явные и согласованные данные на входе ваших функций тоже помогают избежать неожиданностей.
Статья основана на вопросе на StackOverflow от Danylo Kuznetsov и ответе от furas.