У меня есть фреймворк pandas (это всего лишь небольшая часть)
>>> d1
y norm test y norm train len(y_train) len(y_test) \
0 64.904368 116.151232 1645 549
1 70.852681 112.639876 1645 549
SVR RBF \
0 (35.652207342877873, 22.95533537448393)
1 (39.563683797747622, 27.382483096332511)
LCV \
0 (19.365430594452338, 13.880062435173587)
1 (19.099614489458364, 14.018867136617146)
RIDGE CV \
0 (4.2907610988480362, 12.416745648065584)
1 (4.18864306788194, 12.980833914392477)
RF \
0 (9.9484841581029428, 16.46902345373697)
1 (10.139848213735391, 16.282141345406522)
GB \
0 (0.012816232716538605, 15.950164822266007)
1 (0.012814519804493328, 15.305745202851712)
ET DATA
0 (0.00034337162272515505, 16.284800366214057) j2m
1 (0.00024811554516431878, 15.556506191784194) j2m
>>>
Я хочу разбить все столбцы, содержащие кортежи. Например, я хочу заменить столбец LCV
столбцами LCV-a
и LCV-b
.
Как я могу это сделать?
Я обнаружил, что на гораздо больших наборах данных
.apply()
это на несколько порядков медленнее, чемpd.DataFrame(df['b'].values.tolist(), index=df.index)
Эта проблема с производительностью была закрыта в GitHub, хотя я не согласен с этим решением:
https://github.com/pandas-dev/pandas/issues/11615
РЕДАКТИРОВАТЬ: на основе этого ответа: https://stackoverflow.com/a/44196843/2230844
источник
pd.DataFrame(df['b'].tolist())
без них,.values
кажется, тоже работает нормально. (И спасибо, ваше решение намного быстрее.apply()
)str
Сбруя , который доступен дляpandas.Series
объектовdtype == object
фактически итератор.Предположим
pandas.DataFrame
df
:df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))])) df col 0 (a, 10) 1 (b, 20) 2 (c, 30) 3 (d, 40) 4 (e, 50) 5 (f, 60) 6 (g, 70) 7 (h, 80) 8 (i, 90) 9 (j, 100)
Мы можем проверить, является ли он повторяющимся
from collections import Iterable isinstance(df.col.str, Iterable) True
Затем мы можем назначить из него, как и другие итерации:
var0, var1 = 'xy' print(var0, var1) x y
Самое простое решение
Итак, в одной строке мы можем назначить оба столбца
df['a'], df['b'] = df.col.str df col a b 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Более быстрое решение
Только немного сложнее, мы можем использовать
zip
для создания аналогичного итеративногоdf['c'], df['d'] = zip(*df.col) df col a b c d 0 (a, 10) a 10 a 10 1 (b, 20) b 20 b 20 2 (c, 30) c 30 c 30 3 (d, 40) d 40 d 40 4 (e, 50) e 50 e 50 5 (f, 60) f 60 f 60 6 (g, 70) g 70 g 70 7 (h, 80) h 80 h 80 8 (i, 90) i 90 i 90 9 (j, 100) j 100 j 100
В соответствии
Значение: не изменять существующие.
df
Это работает, потому что
assign
принимает аргументы ключевого слова, где ключевые слова являются новыми (или существующими) именами столбцов, а значения будут значениями нового столбца. Вы можете использовать словарь, распаковать его**
и использовать в качестве аргументов ключевого слова. Таким образом, это умный способ присвоения нового столбца с именем,'g'
который является первым элементом вdf.col.str
итерируемом объекте и'h'
вторым элементом вdf.col.str
итерируемом объекте.df.assign(**dict(zip('gh', df.col.str))) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Моя версия
list
подходаС современным пониманием списков и распаковкой переменных.
Примечание: также встроено, используя
join
df.join(pd.DataFrame([*df.col], df.index, [*'ef'])) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Мутирующая версия будет
df[['e', 'f']] = pd.DataFrame([*df.col], df.index)
Наивный тест времени
Короткий DataFrameИспользуйте один, определенный выше
Длинный фрейм данных%timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
В 10 ^ 3 раза больше
df = pd.concat([df] * 1000, ignore_index=True) %timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
источник
df['a'], df['b'] = df.col.str
:)Я думаю, что более простой способ:
>>> import pandas as pd >>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]}) >>> df a b 0 1 (1, 2) 1 2 (3, 4) >>> df['b_a']=df['b'].str[0] >>> df['b_b']=df['b'].str[1] >>> df a b b_a b_b 0 1 (1, 2) 1 2 1 2 (3, 4) 3 4
источник
str
представленияpd.Series
объекта. Вы можете объяснить, как это вообще работает ?!Я знаю, что это было давно, но оговорка второго решения:
pd.DataFrame(df['b'].values.tolist())
заключается в том, что он явно отбрасывает индекс и добавляет последовательный индекс по умолчанию, тогда как принятый ответ
не будет, так как результат применения сохранит индекс строки. Хотя изначально порядок сохраняется в исходном массиве, pandas будет пытаться сопоставить индикаторы из двух фреймов данных.
Это может быть очень важно, если вы пытаетесь установить строки в массив с числовым индексом, а pandas автоматически попытается сопоставить индекс нового массива со старым и вызвать некоторое искажение в упорядочивании.
Лучшим гибридным решением было бы установить индекс исходного фрейма данных на новый, т.е.
pd.DataFrame(df['b'].values.tolist(), index=df.index)
Это сохранит скорость использования второго метода, сохраняя при этом порядок и индексацию результата.
источник