Страницы

Поиск по вопросам

Показаны сообщения с ярлыком dataframe. Показать все сообщения
Показаны сообщения с ярлыком dataframe. Показать все сообщения

вторник, 31 марта 2020 г.

Преобразование Фурье в Pandas

#python #pandas #dataframe #scipy #фурье


Есть DataFrame примерно такого содержания:

Time     Data
95114    1012.0
96078    1108.0
97033    1132.0
98015    1076.0
...


Состоит из 100 строк, где:


Time - время измерения в микросекундах
Data - измеряемое значение. 


Мне необходимо найти ДПФ этой зависимости. Все бы ничего, только частота измерения
непостоянна. Она немного плывет со временем случайным образом. Т.е. мои потуги в духе:

xf = fftfreq(len(df['Time']),1000) # 1000 взята как примерный период измерений
i = xf > 0
yf = fft(df['Data'])


Дают немного неожиданный результат. Я так понимаю, что дело в частоте. Как правильно
ее искать? Интерполировать свой дискретный ряд и потом уже находить его ДПФ? 

В итоге мне нужно будет найти площадь между определенными частотами спектра. 
    


Ответы

Ответ 1



Можно воспользоваться методом DataFrame.resample(), чтобы преобразовать DataFrame во временной ряд с единым интервалом (в качестве единого интервала возьмем средний интервал между замерами в микросекундах) и после этого интерполировать недостающие значения. Для этого надо конвертировать df.Time в datetime dtype и установить этот столбец в качестве индекса: In [62]: freq = int(df['Time'].diff().mean()) # средняя частота измерения в микросекундах In [63]: freq Out[63]: 967 In [64]: df = (df.set_index(pd.to_datetime(df.pop('Time'), unit='us')) .resample('{}U'.format(freq)) .mean() .interpolate()) In [65]: df Out[65]: Data Time 1970-01-01 00:00:00.094766 1012.0 1970-01-01 00:00:00.095733 1108.0 1970-01-01 00:00:00.096700 1132.0 1970-01-01 00:00:00.097667 1076.0 После этого можно попробовать воспользоваться преобразованиями Фурье.

понедельник, 30 марта 2020 г.

Как распределить неопределенную категорию в фиче пропорционально долям известных категорий?

#python #python_3x #pandas #dataframe


Есть датафрейм df. Есть фича feat имеющая три категории cat1, cat2, и немногочисленная
Unknown. 

Я хочу избавиться от Unknown, раскидав ее по cat1 и cat2 так, чтобы она разлетелась
по ним пропорционально их доле в датасете. То есть, если изначально было 45% cat1,
40% cat2 и 5% Unknown, то нужно, чтобы Unknown разлетелась в между cat1 и сat2 в соотношении 9/8.

В документации и блогах ничего подобного не нашел.  
    


Ответы

Ответ 1



Исходный DF: In [67]: %paste df = pd.DataFrame({'feat':np.random.choice(['cat1','cat2','Unknown'], size=100, p=[0.45,0.40,0.15])}) ## -- End pasted text -- In [68]: df.feat.value_counts() Out[68]: cat1 49 cat2 36 Unknown 15 Name: feat, dtype: int64 Сначала найдем соотношение между cat1 и cat2: In [69]: pct = df.feat.value_counts() / len(df) In [70]: ratio = pct.loc[['cat1','cat2']].min() / pct.loc[['cat1','cat2']].max() In [71]: pct Out[71]: cat1 0.49 cat2 0.36 Unknown 0.15 Name: feat, dtype: float64 In [72]: ratio Out[72]: 0.7346938775510204 теперь заменим строки с Unknown с таким же соотношением: In [74]: df.loc[df['feat']=='Unknown', 'feat'] = \ ...: np.random.choice([pct[['cat1','cat2']].idxmax(), pct[['cat1','cat2']].idxmin()], ...: size=df['feat'].eq('Unknown').sum(), ...: p=[ratio, 1-ratio]) ...: ...: In [75]: df.feat.value_counts() Out[75]: cat1 62 cat2 38 Name: feat, dtype: int64 проверка соотношения после замены значений: In [76]: pct.loc[['cat1','cat2']].min() / pct.loc[['cat1','cat2']].max() Out[76]: 0.7346938775510204

Получение строки значений из groupby

#python #pandas #dataframe #group_by


Есть DataFrameGroupby со следующими данными:

                       last  vol
datetime                        
2013-07-23 10:00:00  112450   49
2013-07-23 10:00:00  112440   67
2013-07-23 10:00:00  112430   93
2013-07-23 10:00:00  112420   52
2013-07-23 10:00:00  112410   63

                       last  vol
datetime                        
2013-07-23 10:01:00  112690   17
2013-07-23 10:01:00  112680   59
2013-07-23 10:01:00  112670  226
2013-07-23 10:01:00  112660  184
2013-07-23 10:01:00  112650  289


Сгруппированные по уровню индекса:

blocks_group = datetime_group.groupby(level=0)


Как получить целую строку из каждой группы с максимальным значением, а не только
значения столбца vol?
    


Ответы

Ответ 1



Исходный DataFrame: In [47]: df Out[47]: last vol datetime 2018-08-31 10:00:00 112450 49 2018-08-31 10:00:00 112440 67 2018-08-31 10:00:00 112430 93 2018-08-31 10:00:00 112420 52 2018-08-31 10:00:00 112410 63 2018-08-31 10:01:00 112690 17 2018-08-31 10:01:00 112680 59 2018-08-31 10:01:00 112670 226 2018-08-31 10:01:00 112660 184 2018-08-31 10:01:00 112650 289 Решение: In [48]: df.groupby(level=0, as_index=False).apply(lambda x: x.nlargest(1, 'vol')) Out[48]: last vol datetime 0 2018-08-31 10:00:00 112430 93 1 2018-08-31 10:01:00 112650 289 Ещё один, менее идиоматичный, вариант: In [51]: df.sort_values('vol').groupby(level=0).tail(1) Out[51]: last vol datetime 2018-08-31 10:00:00 112430 93 2018-08-31 10:01:00 112650 289

Столбец по сгруппированным данным Pandas

#python #pandas #dataframe #group_by


Имеется Pandas DataFrame, например:

  city  col1  
0  nsk    15
1  nsk    17
2  nsk    22
3  vdk    11
4  vdk     9


Требуется добавить столбец, в котором будут суммы значений col1, сгруппированные
по столбцу city. Например:

  city  col1  col2
0  nsk    15    54
1  nsk    17    54
2  nsk    22    54
3  vdk    11    20
4  vdk     9    20

    


Ответы

Ответ 1



In [434]: df['col2'] = df.groupby('city')['col1'].transform('sum') In [435]: df Out[435]: city col1 col2 0 nsk 15 54 1 nsk 17 54 2 nsk 22 54 3 vdk 11 20 4 vdk 9 20

воскресенье, 29 марта 2020 г.

Как эффективно объединить несколько pandas DataFrame?

#python #pandas #numpy #dataframe


Готовлю данные для модели, в частности заполняю пропуски для вещественных признаков
и преобразую категориальные. Исходные данные имеют размерность (40000, 230), из них
первые 190 признаков вещественные, остальные — категориальные.


Подготовим вещественные данные для анализа. Заполним пропуски средним

X_data = pd.DataFrame()
for column in data.columns[:num_of_numerical_vars]:
    m1 = data[column].mean()
    if np.isnan(m1):
        m1 = data[column].notnull().mean()
    X_data[column] = data[column].fillna(m1) 

Подготовим категориальные признаки. Воспользуемся LabelEncoder в 
связке с OneHotEncoder

label_encoder = LabelEncoder()
data_categorical_num = data_categorical.apply(lambda col: label_encoder.fit_transform(col))
hot_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
encoded_categorical = hot_encoder.fit_transform(data_categorical_num)



Выводим размерность encoded_categorical

print encoded_categorical.shape


Вывод:


  (40000, 143513)


То есть данных довольно много. Подскажите, как в этом случае объединить получившиеся
вещественные и категориальные признаки эффективно? Если написать что–то вроде:

pd_cats = pd.DataFrame(data=encoded_categorical)
X_data = pd.concat([X_data, pd_cats])


То на моем ПК не хватает памяти для данной операции. Как мне кажется, данные можно
объединить как–то без копирования данных.

Данные могут быть загружены отсюда ("small" dataset)
    


Ответы

Ответ 1



В данном случае стоит использовать разреженные матрицы / фреймы. from sklearn.preprocessing import MultiLabelBinarizer mlb = MultiLabelBinarizer(sparse_output=True) WORK_DIR = Path(r'D:\data\927487') train = pd.read_csv(WORK_DIR / 'orange_small_train.data', sep='\t') labels = pd.read_csv(WORK_DIR / 'orange_small_train_appetency.labels', header=None, squeeze=True, dtype='int8') categorical_cols = train.columns[train.dtypes.eq('object')] train_cat = train[categorical_cols].copy() train = pd.SparseDataFrame(train.drop(categorical_cols, axis=1)) train = train.join( pd.SparseDataFrame( mlb.fit_transform( train_cat.T.apply(lambda c: c.dropna() .str.cat(sep=' ') .split())), default_fill_value=0)) Результат: In [242]: type(train) Out[242]: pandas.core.sparse.frame.SparseDataFrame In [243]: train.memory_usage().sum() Out[243]: 27806864 # NOTE: sparse DF takes only 27 MiB in RAM In [244]: train.shape Out[244]: (50000, 71696)

Ответ 2



Несколько слов об обработке столбцов с преимущественно отсутствующими данными... Если внимательно посмотреть на исходные данные, то можно заметить, что львиная доля (154) столбцов состоит из NaN на 90+%: train = pd.read_csv(WORK_DIR / 'orange_small_train.data', sep='\t') labels = pd.read_csv(WORK_DIR / 'orange_small_train_appetency.labels', header=None, squeeze=True, dtype='int8') оценка количества столбцов состоящих преимущественно из NaN: In [52]: (train.isna().sum() / len(train) > 0.9).sum() Out[52]: 154 мне кажется стоит попробовать построить регрессионную модель в которой данные столбцы не будут учавствовать вовсе и сравнить с той моделью, где такие данные заполнены средним значением (с высокой долей вероятности эти столбцы/признаки не сильно повлияют на результаты предсказываний): In [54]: train = train.loc[:, train.isna().sum() / len(train) <= 0.9] In [55]: train.shape Out[55]: (50000, 76) categorical_cols = train.columns[train.dtypes.eq('object')] train_cat = train[categorical_cols].copy() # drop categorical cols from the train DF train = train.drop(categorical_cols, axis=1) Чтобы заполнить NaN в оставшихся вещественных столбцах можно воспользоваться sklearn.imputer.SimpleImputer. Кроме того стоит нормализовать данные, чтобы все признаки имели одинаковую шкалу величин - для этого существует sklearn.preprocessing.StandardScaler: from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler impute = SimpleImputer(strategy='mean') scale = StandardScaler() train = pd.SparseDataFrame( scale.fit_transform(impute.fit_transform(train)), columns=train.columns, index=train.index, default_fill_value=0) train = train.join( pd.SparseDataFrame( mlb.fit_transform( train_cat.T.apply(lambda c: c.dropna() .str.cat(sep=' ') .split())), default_fill_value=0)) In [82]: train.memory_usage().sum() Out[82]: 21455996 In [83]: train.shape Out[83]: (50000, 71542) In [84]: train.info() RangeIndex: 50000 entries, 0 to 49999 Columns: 71542 entries, Var6 to 71499 dtypes: float64(42), int32(71500) memory usage: 20.5 MB PS также стоит попробовать воспользоваться методом главных компонент (sklearn.decomposition.PCA) для уменьшения размерности (уменьшить число признаков) выборки и сравнить результаты регрессии. PCA позволяет значительно уменьшить число признаков, выбрав самые значимые. Как работает MultilabelBinarizer: In [104]: mlb = MultiLabelBinarizer() In [105]: df = pd.DataFrame({ ...: 'Col1': [10, 20, 30], ...: 'Col2': [['Apple','Orange','Banana'], ['Apple','Grape'], ['Banana']] ...: }) In [106]: df Out[106]: Col1 Col2 0 10 [Apple, Orange, Banana] 1 20 [Apple, Grape] 2 30 [Banana] In [107]: df.join(pd.DataFrame(mlb.fit_transform(df['Col2']), index=df.index, columns=mlb.classes_)) Out[107]: Col1 Col2 Apple Banana Grape Orange 0 10 [Apple, Orange, Banana] 1 1 0 1 1 20 [Apple, Grape] 1 0 1 0 2 30 [Banana] 0 1 0 0

Как в pandas применить метод rolling к нескольким столбцам DataFrame?

#python #python_3x #pandas #dataframe


Подскажите, как правильнее скользящим окном пройтись по датафрейму для нескольких
столбцов? Окно например 3х3.

Я хочу вычислять максимум из значений попадающих в это окно и записать их в новый
столбец. Вот мой код в несколько действий:

x = pd.Series([1, 4, 3, 2, 1, 6, 2, 8, 3, 0, 1, 2, 1, 5])

df = pd.DataFrame({"A": x**2+8, "B": x*8, "C": x+34})
df[['maxA','maxB','maxC']]  = df.loc[:,'A':'C'].rolling(3).max()
df['Max_ABC']  = df.loc[:,['maxA','maxB','maxC']].max(axis=1)

df


Сейчас я использую промежуточное вычисление как на рисунке, используя столбцы ['maxA','maxB','maxC']
только для получения промежуточного результата. Мне видится что это лишний шаг вычисления,
и наверное есть более простой способ решения.


Знаете ли более пандовский метод для работы со скользящим окном, чтобы получить сразу
нужный столбец как на рисунке ниже?


    


Ответы

Ответ 1



In [46]: df['Max_ABC'] = df[['A','B','C']].rolling(3).max().max(axis='columns') In [47]: df Out[47]: A B C Max_ABC 0 9 8 35 NaN 1 12 16 36 NaN 2 17 24 37 37.0 3 24 32 38 38.0 4 33 40 39 40.0 5 44 48 40 48.0 6 57 56 41 57.0 7 72 64 42 72.0 Пояснение: df[['A','B','C']].rolling(3).max() - возвращает максимумы в скользящем окне для всех указанных столбцов: In [51]: df[['A','B','C']].rolling(3).max() Out[51]: A B C 0 NaN NaN NaN 1 NaN NaN NaN 2 17.0 24.0 37.0 3 24.0 32.0 38.0 4 33.0 40.0 39.0 5 44.0 48.0 40.0 6 57.0 56.0 41.0 7 72.0 64.0 42.0 далее мы выбираем максимум для каждой строки, указав axis='columns': In [52]: df[['A','B','C']].rolling(3).max().max(axis='columns') Out[52]: 0 NaN 1 NaN 2 37.0 3 38.0 4 40.0 5 48.0 6 57.0 7 72.0 dtype: float64

Объединение двух таблиц по конкретному столбцу

#python #pandas #dataframe #join


Как грамотно объединить таблицы по столбцу Б? 

На примере будет наглядней. У меня есть таблица А и Б, количество строк не совпадает.

Табл. А: 

   А  Б  
0  1  2  
1  1  3  
2  2  2  
3  2  3  
4  2  1


Табл. Б: 

   Б  В  
0  1  4  
1  2  5  
2  3  6


Требуемый результат:

Табл. В: 

   А  Б  Б  В    
0  1  2  2  5   
1  1  3  3  6   
2  2  2  2  5   
3  2  3  3  6   
4  2  1  1  4  


Как вы сами понимаете, мне требуется "подтянуть" значения из столбца В.
В SQL это решается просто, но как это можно сделать в питоне?
    


Ответы

Ответ 1



Это оч. просто: In [439]: A.merge(B) Out[439]: А Б В 0 1 2 5 1 2 2 5 2 1 3 6 3 2 3 6 4 2 1 4 PS по умолчанию метод DataFrame.merge() объединяет таблицы (DataFrame'ы) по столбцам с совпадающими названиями. Также столбец(ы) для объединения можно указать явно, воспользовавшись параметрами: on left_on right_on Официальная документация о различных способах объединения данных в Pandas

Получить предпоследний заказ для клиента

#python #pandas #dataframe #group_by


У меня есть dataframe с некоторым количеством заказов, id клиента и датой заказа.
Мне нужно посчитать последний выполненный заказ по клиенту для каждой строки

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

import pandas as pd
data=pd.DataFrame({'ID': [ 133853.0,155755.0,149331.0,337270.0,
  775727.0,200868.0,138453.0,738497.0,666802.0,697070.0,128148.0,1042225.0,
  303441.0,940515.0,143548.0],
 'CLIENT':[ 235632.0,231562.0,235632.0,231562.0,734243.0,
   235632.0,235632.0,734243.0,231562.0,734243.0,235632.0,734243.0,231562.0,
   734243.0,235632.0],
 'DATE_START': [ ('2017-09-01 00:00:00'),
   ('2017-10-05 00:00:00'),('2017-09-26 00:00:00'),
   ('2018-03-23 00:00:00'),('2018-12-21 00:00:00'),
   ('2017-11-23 00:00:00'),('2017-09-08 00:00:00'),
   ('2018-12-12 00:00:00'),('2018-11-21 00:00:00'),
   ('2018-12-01 00:00:00'),('2017-08-22 00:00:00'),
   ('2019-02-06 00:00:00'),('2018-02-20 00:00:00'),
   ('2019-01-20 00:00:00'),('2017-09-17 00:00:00')]})
data.groupby('CLIENT').apply(lambda x:max(x['ID']))


В идеале нужно, чтобы было тоже количество строк в dataframe, и был еще столбец в
котором указано значение id заказа, который был последним перед текущим заказом.
    


Ответы

Ответ 1



Попробуйте так: data['prev_id']= data.sort_values('DATE_START').groupby('CLIENT')['ID'].transform(lambda x: x.shift()) Или так: data['prev_id']= data.sort_values('DATE_START').groupby('CLIENT').shift()['ID']

Особенности использования условий в DataFrame

#python #pandas #dataframe #условия


Есть DataFrame, в котором надо заполнить col3 данными из col2 по условию:

mask = ((df['col3'].isnull()) & (df['col1'] == 2))


 col1   col2    col3
0   2   2   NaN
1   3   1   NaN
2   2   1   NaN
3   3   1   NaN
4   2   2   NaN


Вывожу данные, которые должны быть изменены в col3:

mask = ((df['col3'].isnull()) & (df['col1'] == 2))
df.loc[mask]

 col1   col2 col3
0   2   2   NaN
2   2   1   NaN
4   2   2   NaN


Выполняю заполнение:

df.loc[mask, 'col3'] = df['col2']
df


Проверяю результат:

df
 col1   col2 col3
0   2   2   2.0
1   3   1   NaN
2   2   1   1.0
3   3   1   NaN
4   2   2   2.0


Все ОК, но на большом объеме данных так не проверишь.

Повторно использую для этого маску:

df.loc[mask]

 col1 col2  col3
0   2   2   2.0
2   2   1   1.0
4   2   2   2.0


Ожидал, что будет пустой список, т.к. col3 заполнена данными, а в маске существует
условие df['col3'].isnull() - выводить только незаполненные.

Почему так произошло и в чем я ошибся?
    


Ответы

Ответ 1



Логическая ошибка в том, что переменная mask была создана до заполнения col3. In [12]: df Out[12]: col1 col2 col3 0 2 2 2.0 1 3 1 NaN 2 2 1 1.0 3 3 1 NaN 4 2 2 2.0 Создаем логическую маску после обновления: In [13]: mask = ((df['col3'].isna()) & (df['col1'] == 2)) и получаем пустой DataFrame: In [14]: df.loc[mask] Out[14]: Empty DataFrame Columns: [col1, col2, col3] Index: [] PS методы .isnull() / .notnull() являются устаревшими, лучше использовать .isna() / .notna().

суббота, 21 марта 2020 г.

Конвертация pd.Series в pd.Dataframe определенного формата

#python #python_3x #pandas #dataframe #pivot


Существует одномерный массив b

b = pd.Series(
[110.0, 120.0, Horsepower]
[150.0, 175.0, Horsepower]
[1351.0, 1601.0, Capacity]
[12.0, 14.0, Metric Tons]
[1601.0, 1751.0, Capacity])
Name: fiProductClassDesc, dtype: object



Первая цифра списка - минимальное значение
Вторая цифра списка - максимальное значение
Третье значение - название категории (всего 4 шт.)


Необходимо создать из него a = pd.Dataframe, всего 8 колонок, где для каждой строки,
значения будут присвоены только тем колонкам, для которых совпадает название категории
и лэйбла колонки. Остальные 0. 

Digging Depth_min   Digging Depth_max   Metric Tons_min Metric Tons_max Horsepower_min
 Horsepower_max  Capacity_min    Capacity_max
0   0   0   0   110 120 0   0
0   0   0   0   150 175 0   0
0   0   0   0   0   0   1351    1601
0   0   12  14  0   0   0   0
0   0   0   0   0   0   1601    1751

    


Ответы

Ответ 1



Я бы делал это так: x = pd.DataFrame(b.to_list()).rename(columns={0: "min", 1: "max"}) res = x.pivot_table(index=x.index, columns=2, fill_value=0).swaplevel(axis=1) res.columns = res.columns.map("_".join) res = res.sort_index(axis=1, ascending=False) результат: In [115]: res Out[115]: Metric Tons_min Metric Tons_max Horsepower_min Horsepower_max Capacity_min Capacity_max 0 0 0 110 120 0 0 1 0 0 150 175 0 0 2 0 0 0 0 1351 1601 3 12 14 0 0 0 0 4 0 0 0 0 1601 1751 PS думаю добавить нулевые столбцы Digging Depth_min и Digging Depth_max проблем не составит пошаговое решение: In [116]: pd.DataFrame(b.to_list()).rename(columns={0: "min", 1: "max"}) Out[116]: min max 2 0 110.0 120.0 Horsepower 1 150.0 175.0 Horsepower 2 1351.0 1601.0 Capacity 3 12.0 14.0 Metric Tons 4 1601.0 1751.0 Capacity In [117]: x.pivot_table(index=x.index, columns=2, fill_value=0).swaplevel(axis=1) Out[117]: 2 Capacity Horsepower Metric Tons Capacity Horsepower Metric Tons max max max min min min 0 0 120 0 0 110 0 1 0 175 0 0 150 0 2 1601 0 0 1351 0 0 3 0 0 14 0 0 12 4 1751 0 0 1601 0 0 In [120]: res Out[120]: Capacity_max Horsepower_max Metric Tons_max Capacity_min Horsepower_min Metric Tons_min 0 0 120 0 0 110 0 1 0 175 0 0 150 0 2 1601 0 0 1351 0 0 3 0 0 14 0 0 12 4 1751 0 0 1601 0 0 In [121]: res.sort_index(axis=1, ascending=False) Out[121]: Metric Tons_min Metric Tons_max Horsepower_min Horsepower_max Capacity_min Capacity_max 0 0 0 110 120 0 0 1 0 0 150 175 0 0 2 0 0 0 0 1351 1601 3 12 14 0 0 0 0 4 0 0 0 0 1601 1751

Группировка кол-ва выполненных задач по сотрудникам

#python #python_3x #pandas #dataframe #group_by


Как сгруппировать корректно данные? 

Имеется датафрейм со статусом завершенных заданий по каждому аналитику и типу задач:

df1

    ID     Board   Analyst   Status   Crea_d       Fin_d
    46258  RUCRA   Ivanov    open     2019-07-10    NaT
    2345   RUCRA   Ivanov    close    2019-07-11   2019-07-11
    46218  RUCRA   Ivanov    close    2019-07-11   2019-07-11
    3087   RUCRA   Sidorov   open     2019-07-22    NaT
    2367   BV      Petrov    open     2019-07-25    NaT
    2985   GRADE   Petrov    close    2019-07-05   2019-07-05 
    20987  GRADE   Ivanov    close    2019-07-11   2019-07-12
    2396   BV      Sidorov   open     2019-07-29     NaT


Необходимо сгруппировать данные таким образом, чтобы было видно сколько аналитик
выполнил и сколько еще невыполненных заданий по типам (Board) за определенный период
(за день, неделю, месяц).

grouped_df:

      Board   Analyst   Status   Count       
      RUCRA   Ivanov    open     1    
      RUCRA   Ivanov    close    2   
      RUCRA   Sidorov   open     1    
      BV      Petrov    open     1    
      GRADE   Petrov    close    1    
      GRADE   Ivanov    close    1
      BV      Sidorov   open     1     


Я что-то похожее сделал, но где-то нарушена логика, где не могу разобраться:

grouped_df: = (df1.groupby(['Board','Analyst','Status', pd.Grouper(key='Fin_d', freq='M')],
as_index=False)['ID'].count())


Просто дальше я хочу строить графики по каждому аналитику, сколько он по дням выполняет
заданий (bars) с трендовой линией, но то ли из-за ошибки в коде или нарушения логики,
ничего не выходит.
    


Ответы

Ответ 1



Если я правильно понял: In [5]: df1.groupby(["Board", "Analyst", "Status"]).size().reset_index(name="Count") Out[5]: Board Analyst Status Count 0 BV Petrov open 1 1 BV Sidorov open 1 2 GRADE Ivanov close 1 3 GRADE Petrov close 1 4 RUCRA Ivanov close 2 5 RUCRA Ivanov open 1 6 RUCRA Sidorov open 1 или так: In [11]: df1.groupby(["Board", "Analyst", "Status", pd.Grouper(key='Crea_d', freq='MS')]).size().reset_index(name="Count") Out[11]: Board Analyst Status Crea_d Count 0 BV Petrov open 2019-07-01 1 1 BV Sidorov open 2019-07-01 1 2 GRADE Ivanov close 2019-07-01 1 3 GRADE Petrov close 2019-07-01 1 4 RUCRA Ivanov close 2019-07-01 2 5 RUCRA Ivanov open 2019-07-01 1 6 RUCRA Sidorov open 2019-07-01 1

Трансформация DataFrame

#python #pandas #dataframe #group_by #статистика


Есть 2 pd.DataFrame:

a = pd.DataFrame([[1, 20], [2, 25], [3, 24], [1, 22], [2, 29]], columns=['type', 'size'])
b = pd.DataFrame([[1, 19], [1, 21], [2, 24], [4, 21]], columns=['type', 'size'])


В каждый DataFrame хочу добавить столбцы следующего типа

a['amount_mean_type'] = a['size'] / a.groupby('type')['size'].transform('mean')
a['amount_std_type'] = a['size'] / a.groupby('type')['size'].transform('std')


Вопрос в том, как мне сделать так, чтобы в b['amount_mean_type'] и b['amount_std_type']
я использовал mean и std по группам из a, а по тем категориям, которых нет в a, использовать
mean и std по всему сету (если это корректно). 

Что-то вроде этого: 

b['amount_mean_type'] = b['size'] / a.groupby('type')['size'].transform('mean')
b['amount_std_type'] = b['size'] / a.groupby('type')['size'].transform('std')


Но только с теми условиями, которые описал выше

UPD: пример фреймов на выходе

Для a все просто:

a['amount_mean_type'] = a['size'] / a.groupby('type')['size'].transform('mean')
a['amount_std_type'] = a['size'] / a.groupby('type')['size'].transform('std')

  type size amount_mean_type amount_std_type
0   1   20  0.952381    14.142136
1   2   25  0.925926    8.838835
2   3   24  1.000000    NaN 
3   1   22  1.047619    15.556349
4   2   29  1.074074    10.253048


для b приходится прописать вручную каждую операцию

b.loc[0, 'amount_mean_type'] = b.loc[0, 'size'] / a[a['type'] == 1]['size'].mean()
b.loc[1, 'amount_mean_type'] = b.loc[1, 'size'] / a[a['type'] == 1]['size'].mean()
b.loc[2, 'amount_mean_type'] = b.loc[2, 'size'] / a[a['type'] == 2]['size'].mean()
b.loc[3, 'amount_mean_type'] = b.loc[3, 'size'] / a['size'].mean()

b.loc[0, 'amount_std_type'] = b.loc[0, 'size'] / a[a['type'] == 1]['size'].std()
b.loc[1, 'amount_std_type'] = b.loc[1, 'size'] / a[a['type'] == 1]['size'].std()
b.loc[2, 'amount_std_type'] = b.loc[2, 'size'] / a[a['type'] == 2]['size'].std()
b.loc[3, 'amount_std_type'] = b.loc[3, 'size'] / a['size'].std()

  type size amount_mean_type amount_std_type
0   1   19  0.904762    13.435029
1   1   21  1.000000    14.849242
2   2   24  0.888889    8.485281
3   4   21  0.875000    6.192562 # Считается по всей переменной 'type', так как категории
(4) нет в `a`


Фреймы a и b можно рассматривать как train и test соответственно
    


Ответы

Ответ 1



tmp = a.groupby('type')['size'].agg(["mean", "std"]) res = (b.assign(x=b["type"].map(tmp["mean"]).fillna(a["size"].mean())) .eval("amount_mean_type = size / x") .drop(columns="x")) res = (res.assign(x=b["type"].map(tmp["std"]).fillna(a["size"].std())) .eval("amount_std_type = size / x") .drop(columns="x")) результат: In [28]: res Out[28]: type size amount_mean_type amount_std_type 0 1 19 0.904762 13.435029 1 1 21 1.000000 14.849242 2 2 24 0.888889 8.485281 3 4 21 0.875000 6.192562

Как удалить столбцы, у которых кол-во пропущенных значений больше 2 согласно прилагаемого алгоритма

#python #pandas #dataframe


Алгоритм удаления по заданию следующий:


найдите все такие столбцы (метод .isna() + подвыборка с условием)
определите их названия (поймите как выцепить названия и сохраните их в массив, т.е.
по цепочке примените методы .index, .tolist() или .values)
"дропните их" (используйте метод .drop (имена столбцов))
результат сохраните в новой табличке (например, data_1), исходную менять не нужно


Сделала по-другому:

cols_to_delete = df2.columns[df2.isna().sum() > 2]
df2.drop(cols_to_delete, axis = 1, inplace = True)


Что мне непонятно: как сделать задание по вышеуказанному алгоритму #1 - #4.

df = pd.DataFrame({
    'col1': [23, 54, pd.np.nan, 87],
    'col2': [45, 39, 45, 32],
    'col3': [pd.np.nan, pd.np.nan, 76, pd.np.nan,]
})

    


Ответы

Ответ 1



Исходный DF: In [92]: df Out[92]: col1 col2 col3 0 23.0 45 NaN 1 54.0 39 NaN 2 NaN 45 76.0 3 87.0 32 NaN Решение 1: In [93]: thresh = 2 In [94]: new = df.loc[:, df.isna().sum().le(thresh)] Решение 2: In [96]: new = df.drop(columns=df.columns[df.isna().sum() > thresh]) Результат: In [95]: new Out[95]: col1 col2 0 23.0 45 1 54.0 39 2 NaN 45 3 87.0 32

воскресенье, 1 марта 2020 г.

почему появляется столбец 'Unnamed'

#python #pandas #dataframe #csv


я создал датафрейм , в нём 3 колнки. Перегоняю его в файл. Читаю данные с файла и
вижу новый столбец 'Unnamed' . Всё бы ничего , но я не могу его удалить , вылетает ошибка:

ValueError: labels ['Unnamed'] not contained in axis


откуда вообще он взялся и что с ним делать ?
    


Ответы

Ответ 1



Скорее всего вы сохранили CSV файл с индексом (по умполчанию). Как это проверить: In [31]: df.columns.tolist() Out[31]: ['Unnamed: 0', 'a', 'b', 'c'] Пример: In [16]: df = pd.DataFrame(np.arange(1,10).reshape(3,3), columns=list('abc')) In [17]: df Out[17]: a b c 0 1 2 3 1 4 5 6 2 7 8 9 In [18]: df.to_csv(r'c:/temp/test.csv') теперь если просто прочитать его, то получим столбец Unnamed: 0 (т.к. индекс был безымянным): In [20]: x = pd.read_csv(r'c:/temp/test.csv') In [21]: x Out[21]: Unnamed: 0 a b c 0 0 1 2 3 1 1 4 5 6 2 2 7 8 9 вот как это выглядит в CSV файле: In [22]: print(Path(r'c:/temp/test.csv').read_text()) ,a,b,c 0,1,2,3 1,4,5,6 2,7,8,9 Как это обойти: In [23]: x = pd.read_csv(r'c:/temp/test.csv', index_col=0) In [24]: x Out[24]: a b c 0 1 2 3 1 4 5 6 2 7 8 9 или сохранять DataFrame без индекса: In [25]: df.to_csv(r'c:/temp/test.csv', index=False) In [26]: print(Path(r'c:/temp/test.csv').read_text()) a,b,c 1,2,3 4,5,6 7,8,9 In [27]: pd.read_csv(r'c:/temp/test.csv') Out[27]: a b c 0 1 2 3 1 4 5 6 2 7 8 9

Случайный лес и бэггинг деревьев решений

#python #pandas #машинное_обучение #dataframe #numpy


Запрограммировала случайный лес и бэггинг деревьев решений. 

Что-то видимо не правильно потому, что процент несовпадений выдает одинаковое число
для трех методов, и при изменении выборки ничего не происходит.

Как правильно должна выглядеть программа? Надо ли отдельно реализовать бэггинг и
деревья решений?

Код:

 import numpy as np
 import pandas as pd
 from sklearn.model_selection import cross_val_score
 from sklearn.cross_validation import train_test_split
 from sklearn.ensemble.forest import RandomForestClassifier
 from sklearn.ensemble import BaggingClassifier
 from sklearn.tree import DecisionTreeClassifier
data = pd.read_csv('C:\\Users\\Vika\\Downloads\\1500.csv', ";",decimal =',', skipinitialspace=True)
 data.head()
 kfold = 10
 itog_val1 = {} 
 itog_val2 = {} 
 itog_val3 = {} 
 itog_val4 = {} 
 itog_val5 = {} 
 itog_val6 = {}
 X = data.drop('Y1', axis=1).values[:, :8] 
 y = data['Y1'].values
 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
 print ('обучающая выборка X:\n', X_train[:6])
 print ('\n')
 print ('y_train У:\n', y_train[:10])
 print (' \n')
 print ('тестовая выборка Х:\n', X_test[:6])
 print ('\n')
 print ('y_test У\n', y_test[:10])
 first_tree = RandomForestClassifier(n_estimators=100)  
 scores = cross_val_score(first_tree, X_train, y_train, cv=kfold) 
 itog_val1 = scores 
 print(itog_val1)  
 first_tree1 = DecisionTreeClassifier(max_depth=1)    
 scores = cross_val_score(first_tree, X_train, y_train, cv=kfold)
 itog_val4 = scores 
 print(itog_val4)
 first_tree.fit(X_train, y_train) 
 first_tree.score(X_test, y_test)
 y_test_predicted = first_tree.predict(X_test) 
 print ('RandomForestClassifier X\n', X_test[:9])
 print ('\n')
 print ('RandomForestClassifier y\n', y_test[:11]) 
 first_tree1.fit(X_train, y_train)
 first_tree1.score(X_test, y_test)
 y_test_predicted = first_tree1.predict(X_test)
 print ('DecisionTreeClassifier X\n', X_test[:9])
 print ('\n')
 print (' DecisionTreeClassifier y\n', y_test[:11])
 first_bagging.fit(X_train, y_train)
 first_bagging.score(X_test, y_test)
 y_test_predicted = first_bagging.predict(X_test)
 print ('BaggingClassifier X\n', X_test[:9])
 print ('\n')
 print (' BaggingClassifier y\n', y_test[:11])
 res = pd.DataFrame(np.column_stack((y_test, y_test_predicted, X_test)), 
               columns=['Y1','YR'] + data.columns[1:9].tolist())
 (res['Y1']!= res['YR']).mean()
 res1 = pd.DataFrame(np.column_stack((y_test, y_test_predicted, X_test)), 
               columns=['Y1','YD'] + data.columns[1:9].tolist())
 (res1['Y1']!= res1['YD']).mean() 
 res2 = pd.DataFrame(np.column_stack((y_test, y_test_predicted, X_test)), 
               columns=['Y1','YB'] + data.columns[1:9].tolist())
 (res2['Y1']!= res2['YB']).mean() 

    


Ответы

Ответ 1



Вы создали три DataFrame из одних и тех же данных: res = pd.DataFrame(np.column_stack((y_test, y_test_predicted, X_test)), columns=['Y1','YR'] + data.columns[1:9].tolist()) ... res1 = pd.DataFrame(np.column_stack((y_test, y_test_predicted, X_test)), columns=['Y1','YD'] + data.columns[1:9].tolist()) ... res2 = pd.DataFrame(np.column_stack((y_test, y_test_predicted, X_test)), columns=['Y1','YB'] + data.columns[1:9].tolist()) как следствие процент несовпадений - одинаковый

Как преобразовать логические False, True в 1, 0?

#python #python_3x #pandas #dataframe #numpy


Есть ли функция для преобразования булевых значений в числовые? 

Только мне нужно, чтобы False -> 1, а True -> 0.
В принципе можно и replace использовать, но хочется найти более красивый способ.
    


Ответы

Ответ 1



Пример: Исходный DataFrame: In [19]: df = pd.DataFrame({'col':np.random.choice([True, False], 10)}) In [20]: df Out[20]: col 0 False 1 True 2 True 3 False 4 False 5 True 6 False 7 False 8 True 9 False решение: In [22]: df['col'] = (-df['col']).astype(np.int8) In [23]: df Out[23]: col 0 1 1 0 2 0 3 1 4 1 5 0 6 1 7 1 8 0 9 1 In [24]: df.dtypes Out[24]: col int8 dtype: object альтернативный вариант: In [27]: df['col'] = (~df['col']).astype(np.uint8) In [28]: df Out[28]: col 0 1 1 0 2 0 3 1 4 1 5 0 6 1 7 1 8 0 9 1

среда, 26 февраля 2020 г.

Как получить данные колонки индексов Pandas DataFrame?

#python #pandas #dataframe #matplotlib


Считываю данные:

data = pd.read_csv('data.csv',';', index_col=['month'], parse_dates=['month'], dayfirst=True)


Смотрю на них:

print data.shape
print data.columns
data.head()


Вывод:

(168, 1)
Index([u'milk'], dtype='object')


   month    milk
1962-01-01  589
1962-02-01  561
1962-03-01  640
1962-04-01  656
1962-05-01  727


В какой-то момент мне необходимо построить график количества во времени. Каким образом
получить данные колонки индексов (month)? Напомню, что мы ее указали индексом и в данных
у нас только одна колонка с количеством. Конечно, можно отобразить как data.plot(),
но мне хотелось бы сделать это вызвав plot у matplotlib.

Дополнительно вопрос о лучших практиках: в каких случаях обоснованно / следует использовать
данные в качестве индекса?
    


Ответы

Ответ 1



Судя по выводу data.head(), вы либо не указали параметр index_col=['month'] либо он отработал не так как ожидалось и столбец month оказался обычным столбцом, вместо того чтобы стать индексом. Чтобы получить значения индекса DataFrame'а, достаточно воспользоваться атрибутом DataFrame.index. Пример: In [129]: df = pd.DataFrame({'val':np.random.rand(100)}, index=np.arange(100, 200)) In [130]: df Out[130]: val 100 0.924396 101 0.960312 102 0.728403 103 0.435330 104 0.599777 .. ... 195 0.055345 196 0.560087 197 0.049506 198 0.819265 199 0.231141 [100 rows x 1 columns] In [131]: plt.plot(df.index, df['val']) Out[131]: [] UPDATE: если на оси X - дата/время, то проще воспользоваться Pandas для визуализации: In [154]: df = pd.read_csv(r'C:\download\data.csv', sep=';', parse_dates=True, index_col=0) In [155]: df Out[155]: milk month 1962-01-01 589 1962-02-01 561 1962-03-01 640 1962-04-01 656 1962-05-01 727 In [156]: df.plot(y='milk') Out[156]: Значения индекса DataFrame: In [157]: df.index Out[157]: DatetimeIndex(['1962-01-01', '1962-02-01', '1962-03-01', '1962-04-01', '1962-05-01'], dtype='datetime64[ns]', name='month', freq=None)

Как лучше рассчитать экспоненциальное скользящее среднее?

#python #pandas #dataframe #python_35


Как лучше рассчитать экспоненциальное скользящее среднее?
    


Ответы

Ответ 1



Судя по вашим предыдущим вопросам, вы работаете с Pandas DataFrame. Воспользуйтесь методом DataFrame.ewm(...): >>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]}) B 0 0.0 1 1.0 2 2.0 3 NaN 4 4.0 >>> df.ewm(com=0.5).mean() B 0 0.000000 1 0.750000 2 1.615385 3 1.615385 4 3.670213

Вычисление данных в сгруппированном Data Frame

#python #python_3x #pandas #dataframe #group_by


Есть два DF:

df_estim = pd.DataFrame({'estim': ['est1', 'est2', 'est3'],
                   'title': ['title1', 'title2', 'title3'],
                    'key': ['key1', 'key2', 'key3'],                   
                   'mount': [300.15, 350.85, 400.47],
                   'equip': [870.35, 1750.59, 1830.80]})
df_estim


    estim   title   key     mount   equip
0   est1    title1  key1    300.15  870.35
1   est2    title2  key2    350.85  1750.59
2   est3    title3  key3    400.47  1830.80



df_equip = pd.DataFrame({'sys_2': ['sys1', 'sys1', 'sys1', 'sys1', 'sys1', 'sys1',
'sys2', 'sys2', 'sys2', 'sys2',
                                   'sys2', 'sys2', 'sys2', 'sys2', 'sys3', 'sys3',
'sys3', 'sys3', 'sys3', 'sys3',
                                  'sys3', 'sys3'],                    
                    'block_2': [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 
                                1, 2, 2, 2, 0, 0, 0, 1, 1, 2,
                               2, 2],
                    'kks_2': ['kks1', 'kks2', 'kks3', 'kks4', 'kks5', 'kks6', 'kks7',
'kks8', 'kks9', 'kks10',
                              'kks11', 'kks12', 'kks13', 'kks14', 'kks15', 'kks16',
'kks17', 'kks18', 'kks19', 'kks20',
                             'kks21', 'kks22'],
                    'key': ['key1', 'key1', 'key2', 'key2', 'key3', 'key3', 'key1',
'key1', 'key2', 'key3', 
                            'key3', 'key2', 'key1', 'key1', 'key2', 'key2', 'key3',
'key2', 'key3', 'key2',
                           'key3', 'key3'],
                    'price_2': [100.10, 110.10, 120.10, 130.10, 140.10, 150.10, 160.10,
170.10, 180.10, 190.10, 
                                200.10, 210.10, 220.10, 230.10, 240.10, 250.10, 260.10,
270.10, 280.10, 290.10, 
                                300.10, 310.10]
                           })
df_equip[:5]


    sys_2 block_2 kks_2 key     price_2
0   sys1    0   kks1    key1    100.1
1   sys1    0   kks2    key1    110.1
2   sys1    0   kks3    key2    120.1
3   sys1    0   kks4    key2    130.1
4   sys1    0   kks5    key3    140.1


Для дальнейшего объяснения делаю объединение:

merge_df = pd.merge(df_equip, df_estim,  how='inner', left_on='key', right_on='key', 
                    left_index=False)
merge_df[:5]


    sys_2 block_2 kks_2 key     price_2 estim   title   mount   equip
0   sys1    0   kks1    key1    100.1   est1    title1  300.15  870.35
1   sys1    0   kks2    key1    110.1   est1    title1  300.15  870.35
2   sys2    1   kks7    key1    160.1   est1    title1  300.15  870.35
3   sys2    1   kks8    key1    170.1   est1    title1  300.15  870.35
4   sys2    2   kks13   key1    220.1   est1    title1  300.15  870.35


Нужно из полученных вспомогательных данных (ниже):

merge_df.groupby(['estim', 'equip'])['price_2'].sum()

estim  equip  
est1   870.35      990.6
est2   1750.59    1690.8
est_3  1830.80    1830.8


вычислить отношение по каждой записи estim между данными колонки equip и ['price_2'].sum(),
и поместить их в колонку coeff (например, для est1: 870.35/990.6 = 0.8786). Коэффициент
необходим для расчета данных в новой колонке estim_sys. Порядок расчета для est1: сумма
['price_2'].sum() / коэффициент для est1 = 0.8786. Итого: 239.24:

merge_df.groupby(['sys_2', 'block_2', 'estim'])['price_2'].sum()

sys_2  block_2  estim   estim_sys
sys1   0        est1     239.24       210.2
                est2                  250.2
                est3                  290.2
sys2   1        est1                  330.2
                est2                  180.1
                est3                  390.2
       2        est1                  450.2
                est2                  210.1
sys3   0        est2                  490.2
                est3                  260.1
       1        est2                  270.1
                est3                  280.1
       2        est2                  290.1
                est3                  610.2



И еще прошу подсказать, как сохранить результаты (['price_2'].sum()) в стационарную
колонку, например cost_equip, что бы к ней можно было обращаться.
    


Ответы

Ответ 1



Попробуйте так: m = merge_df.copy() m["coeff"] = m["equip"] / m.groupby(['estim', 'equip'])['price_2'].transform("sum") res = (m .groupby(['sys_2', 'block_2', 'estim']) .apply(lambda x: x["price_2"].sum() / x["coeff"]) .reset_index(name="estim_sys")) In [32]: res Out[32]: sys_2 block_2 estim level_3 estim_sys 0 sys1 0 est1 0 239.241822 1 sys1 0 est1 1 239.241822 2 sys1 0 est2 6 241.654619 3 sys1 0 est2 7 241.654619 4 sys1 0 est3 14 290.200000 5 sys1 0 est3 15 290.200000 6 sys2 1 est1 2 375.821359 7 sys2 1 est1 3 375.821359 8 sys2 1 est2 8 173.948829 9 sys2 1 est3 16 390.200000 10 sys2 1 est3 17 390.200000 11 sys2 2 est1 4 512.400896 12 sys2 2 est1 5 512.400896 13 sys2 2 est2 9 202.924203 14 sys3 0 est2 10 473.457611 15 sys3 0 est2 11 473.457611 16 sys3 0 est3 18 260.100000 17 sys3 1 est2 12 260.874951 18 sys3 1 est3 19 280.100000 19 sys3 2 est2 13 280.191867 20 sys3 2 est3 20 610.200000 21 sys3 2 est3 21 610.200000 Как образовалась колонка "level_3"? в столбце level_3 - значения оригинального индекса из merge_df DataFrame. Как правильно добавить колонку "estim_sys" из "res" в "m"? здесь мы как раз можем использовать столбец "level_3", чтобы Pandas смог выровнять данные по индексам при присваивании: In [42]: m["estim_sys"] = res.set_index("level_3")["estim_sys"] In [43]: m Out[43]: sys_2 block_2 kks_2 key price_2 ... title mount equip coeff estim_sys 0 sys1 0 kks1 key1 100.1 ... title1 300.15 870.35 0.878609 239.241822 1 sys1 0 kks2 key1 110.1 ... title1 300.15 870.35 0.878609 239.241822 2 sys2 1 kks7 key1 160.1 ... title1 300.15 870.35 0.878609 375.821359 3 sys2 1 kks8 key1 170.1 ... title1 300.15 870.35 0.878609 375.821359 4 sys2 2 kks13 key1 220.1 ... title1 300.15 870.35 0.878609 512.400896 5 sys2 2 kks14 key1 230.1 ... title1 300.15 870.35 0.878609 512.400896 6 sys1 0 kks3 key2 120.1 ... title2 350.85 1750.59 1.035362 241.654619 7 sys1 0 kks4 key2 130.1 ... title2 350.85 1750.59 1.035362 241.654619 8 sys2 1 kks9 key2 180.1 ... title2 350.85 1750.59 1.035362 173.948829 9 sys2 2 kks12 key2 210.1 ... title2 350.85 1750.59 1.035362 202.924203 10 sys3 0 kks15 key2 240.1 ... title2 350.85 1750.59 1.035362 473.457611 11 sys3 0 kks16 key2 250.1 ... title2 350.85 1750.59 1.035362 473.457611 12 sys3 1 kks18 key2 270.1 ... title2 350.85 1750.59 1.035362 260.874951 13 sys3 2 kks20 key2 290.1 ... title2 350.85 1750.59 1.035362 280.191867 14 sys1 0 kks5 key3 140.1 ... title3 400.47 1830.80 1.000000 290.200000 15 sys1 0 kks6 key3 150.1 ... title3 400.47 1830.80 1.000000 290.200000 16 sys2 1 kks10 key3 190.1 ... title3 400.47 1830.80 1.000000 390.200000 17 sys2 1 kks11 key3 200.1 ... title3 400.47 1830.80 1.000000 390.200000 18 sys3 0 kks17 key3 260.1 ... title3 400.47 1830.80 1.000000 260.100000 19 sys3 1 kks19 key3 280.1 ... title3 400.47 1830.80 1.000000 280.100000 20 sys3 2 kks21 key3 300.1 ... title3 400.47 1830.80 1.000000 610.200000 21 sys3 2 kks22 key3 310.1 ... title3 400.47 1830.80 1.000000 610.200000 [22 rows x 11 columns]

Скорость работы apply в Pandas

#python #pandas #оптимизация #dataframe #group_by


Имеется dataframe из двух столбцов.
Группируя по первому, суммирую значения по втором. Делаю двумя способами

df.groupby('col1')['col2'].sum()


Скорость выполнения: 0,01 сек

df.groupby('col1')['col2'].apply(lambda x: x.sum())


Скорость выполнения: 4,15 сек

Почему такая разница в скорости? 
Как ускорить работу, если нужна будет не просто сумма, а более сложная функция?
Например, 

df.groupby('col1')['col2'].apply(lambda x: ','.join([str(i) for i in x]))

    


Ответы

Ответ 1



Series.apply() и DataFrame.apply(...) - являются "не совсем векторизированными" функциями, которые чаще всего значительно медленнее своих векторизированных аналогов. К сожалению, серебрянной пули универсального и быстрого решения не существует. Так что подход обычно следующий: если есть вектроизированная функция для решения нашей конкретной задачи, то используем ее сравниваем скорость работы .apply() и обычного list comprehension и выбираем самый быстрый вариант. NOTE: при работе со строками (object dtype) list comprehension часто оказывается быстрее .apply() и иногда быстрее соответствующих векторизированных Series.str. методов если скорость не устраивает то пробуем один из подходов, рекоммендованных разработчиками Pandas: Cython Numba pd.eval()