Как векторизировать следующее, добавление, вероятно, является узким местом

#python #pandas #dataframe #vectorization

#питон #панды #фрейм данных #векторизация

Вопрос:

У меня есть следующий фрейм данных с двумя столбцами c1 , и c2 я хочу добавить новый столбец c3 , основанный на следующей логике, то, что у меня есть, работает, но медленно, может ли кто-нибудь предложить способ векторизации этого?

  • Должны быть сгруппированы на основе c1 , а c2 затем для каждой группы новый столбец c3 должен заполняться последовательно, начиная с values того места, где ключ является значением c1 , и каждая «подгруппа» будет иметь последующие значения, IOW values[value_of_c1][idx] , где idx находится «подгруппа», пример ниже
  • Первая группа (1, 'a') , здесь c1 1 , индекс «подгруппы» "a" 0 равен (первая подгруппа 1), поэтому c3 для всех строк в этой группе values[1][0]
  • Вторая группа (1, 'b') здесь c1 все еще 1 есть, но «подгруппа» — "b" это индекс 1 so (вторая подгруппа из 1), поэтому для всех строк в этой группе c3 values[1][1]
  • Третья группа (2, 'y') здесь c1 теперь 2 , «подгруппа» "a" , и индекс 0 (первая подгруппа из 2), поэтому для всех строк в этой группе c3 values[2][0]
  • И так далее
  • values будут иметь необходимые элементы, чтобы удовлетворить этой логике.

Код

 import pandas as pd  df = pd.DataFrame(  {  "c1": [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2],  "c2": ["a", "a", "a", "b", "b", "b", "y", "y", "y", "z", "z", "z"],  } ) new_df = pd.DataFrame() values = {1: ["a1", "a2"], 2: ["b1", "b2"]} for i, j in df.groupby("c1"):  for idx, (k, l) in enumerate(j.groupby("c2")):  l["c3"] = values[i][idx]  new_df = new_df.append(l)  

Вывод (работает, но мой код работает медленно)

 c1 c2 c3 0 1 a a1 1 1 a a1 2 1 a a1 3 1 b a2 4 1 b a2 5 1 b a2 6 2 y b1 7 2 y b1 8 2 y b1 9 2 z b2 10 2 z b2 11 2 z b2  

Ответ №1:

Если вы не возражаете использовать другую библиотеку, вам в основном нужно кодировать надписи в своих группах:

 from sklearn.preprocessing import LabelEncoder  def le(x):  return pd.DataFrame(LabelEncoder().fit_transform(x),index=x.index)   df['idx'] = df.groupby('c1')['c2'].apply(le)  df['c3'] = df.apply(lambda x:values[x['c1']][x['idx']],axis=1)   c1 c2 idx c3 0 1 a 0 a1 1 1 a 0 a1 2 1 a 0 a1 3 1 b 1 a2 4 1 b 1 a2 5 1 b 1 a2 6 2 y 0 b1 7 2 y 0 b1 8 2 y 0 b1 9 2 z 1 b2 10 2 z 1 b2 11 2 z 1 b2  

В противном случае это вопрос использования pd.Categorical той же концепции , что и выше, просто вы преобразуете столбец в каждой группе в категорию и просто вытаскиваете код:

 def le(x):  return pd.DataFrame(pd.Categorical(x).codes,index=x.index)  

Комментарии:

1. спасибо за альтернативное решение, я рассчитал это с помощью pd.Categorical предложения, и оно, похоже, быстрее, чем у меня есть

2. пока я пытаюсь понять это, не могли бы вы сообщить мне, зависит ли это от values данных, которые я показал, или это общее решение? например, если значения values перетасованы и отличаются, это все равно будет работать? это не учитывает шаблон примера игрушки, верно?

3. nvm, это работает, [x['c1']][x['idx']] я думал, что это объединение некоторых столбцов , но я не заметил values , что в values[x['c1']][x['idx']] , еще раз спасибо, это работает

4. круто, хорошо, что ты это понял.. да, часть словаря немного тяжеловата для глаз

Ответ №2:

 In [203]: a = pd.DataFrame([[k, value, idx] for k,v in values.items() for idx,value in enumerate(v)], columns=['c1', 'c3', 'gr'])  ...: b = df.assign(gr=df.groupby(['c1']).transform(lambda x: (x.ne(x.shift()).cumsum())- 1))  ...: print(b)  ...: b.merge(a).drop(columns='gr')  ...: # b  c1 c2 gr 0 1 a 0 1 1 a 0 2 1 a 0 3 1 b 1 4 1 b 1 5 1 b 1 6 2 y 0 7 2 y 0 8 2 y 0 9 2 z 1 10 2 z 1 11 2 z 1 Out[203]:  c1 c2 c3 0 1 a a1 1 1 a a1 2 1 a a1 3 1 b a2 4 1 b a2 5 1 b a2 6 2 y b1 7 2 y b1 8 2 y b1 9 2 z b2 10 2 z b2 11 2 z b2  

Комментарии:

1. спасибо за решение, вы можете это объяснить? Я с радостью приму это

2. 1. df.assign(gr=...) это то же самое, что df['gr'] = ...

3. Я новичок в панд, но, судя по вашим усилиям, насколько сложной была эта проблема?

4. @Valencia Я не могу говорить за Asish, такие проблемы, как эти, довольно просто решить для более опытных пользователей pandas.

5. Я рассчитал этот код с помощью timeit, но почему это медленнее, чем у меня, для меня это выглядит полностью векторизованным и прекрасным