Скажем, у меня есть фрейм данных, содержащий строки, например:
df = pd.DataFrame({'col1':list('some_string')})
col1
0 s
1 o
2 m
3 e
4 _
5 s
...
Я ищу способ применить скользящее окно к col1
и соединить строки в окне определенного размера. Скажем, например, window=3
, я хотел бы получить (без минимального количества наблюдений):
col1
0 s
1 so
2 som
3 ome
4 me_
5 e_s
6 _st
7 str
8 tri
9 rin
10 ing
Я пробовал очевидные решения с rolling
, которые не справляются с обработкой типов объектов:
df.col1.rolling(3, min_periods=0).sum()
df.col1.rolling(3, min_periods=0).apply(''.join)
Оба поднимают:
cannot handle this type -> object
Существует ли универсальный подход для этого (без использования shift
для соответствия этому конкретному случаю w=3
)?
Да, похоже, что это так... Я также пытался придумать хакерское решение, как вы говорите @jezrael, которое также оказалось довольно сложным.
Хорошо, я думаю, если вы действительно думаете, что это проблема с моим вторым ответом, вы можете свободно редактировать вас, восстановить, и я могу удалить его из своего ответа.
@yatu - Это была моя вторая идея, так что вы думаете, я не напишу свое решение, если не опубликую ваш ответ? Но неважно, вам решать, что вы будете делать сейчас.
да, вот моя проблема слишком похожа на ваше решение (если больше данных, вы не проблема, исправьте это). Так что у меня нет проблем в этой ситуации, удалите его из моего ответа, только вам нужно создать ответ
вы нашли решение для этого?
Роллинг работает только с цифрами:
def _prep_values(self, values=None, kill_inf=True): if values is None: values = getattr(self._selected_obj, 'values', self._selected_obj) # GH #12373 : rolling functions error on float32 data # make sure the data is coerced to float64 if is_float_dtype(values.dtype): values = ensure_float64(values) elif is_integer_dtype(values.dtype): values = ensure_float64(values) elif needs_i8_conversion(values.dtype): raise NotImplementedError... ... ...
Поэтому вы должны построить его вручную. Вот один из возможных вариантов с простым пониманием списка (возможно, существует способ, более похожий на Pandas):
df = pd.DataFrame({'col1':list('some_string')})
pd.Series([
''.join(df.col1.values[max(i-2, 0): i+1])
for i in range(len(df.col1.values))
])
0 s 1 so 2 som 3 ome 4 me_ 5 e_s 6 _st 7 str 8 tri 9 rin 10 ing dtype: object
Да, я думаю, что возвращение к list-comp было альтернативой, которую я тоже имел в виду, так как не мог придумать ничего другого. Спасибо, что поделились фрагментом, где это упоминается
Использование pd.Series.cumsum
похоже на работу (хотя и немного неэффективную):
df['col1'].cumsum().str[-3:]
Выход:
0 s
1 so
2 som
3 ome
4 me_
5 e_s
6 _st
7 str
8 tri
9 rin
10 ing
Name: col1, dtype: object
Да я тоже об этом подумал. Проблема в том, что это будет очень неэффективно с точки зрения использования памяти, так как нарезка происходит только после вычисления кумулятивной суммы по всем предыдущим строкам.
Это работает, только если значения являются символами. В этом случае трюк с общей суммой может сработать лучше.
Как насчет смены серии?
df.col1.shift(2).fillna('') + df.col1.shift().fillna('') + df.col1
Обобщение на любое число:
pd.concat([df.col1.shift(i).fillna('') for i in range(3)], axis=1).sum(axis=1)
Как уже упоминалось, not using shift to match this specific case of w=3
Извините, я слишком медленно добавлял динамическую версию.
Хороший подход @IanS
roll может работать только с числовыми типами, и поэтому вывод вычислений для окна должен быть единственным значением.