Материал представляет некоторые методы работы с данными при использовании библиотеки Pandas. В качестве примеров используются наборы данных, связанные с розничной торговлей.
- Рассматриваемые методы :
- Индексация [],loc,iloc
- Срезы
- Логическое индексирование
- Методы isin(),query()
- Случайная выборка методом sample() с весами и без
- Метод value_counts() с нормализацией и без
- Методы concat(),map()
- Лямбда функции и метод apply()
Методы numpy : where(),select(),random.choice()
df = pd.read_excel("mydata/city_shops83.xlsx",index_col="shop")
df
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh01 | city7 | 140403 | 4546 | 410062 | 8 | 14 | mall |
sh02 | city8 | 83276 | 4426 | 363584 | 8 | 14 | mall |
... | ... | ... | ... | ... | ... | ... | ... |
sh82 | city9 | 49295 | 1587 | 285435 | 4 | 4 | dep_store |
sh83 | city6 | 49087 | 2017 | 271771 | 6 | 3 | dep_store |
83 rows × 7 columns
Обозначения :
- shop - индекс магазина
- city - индекс города
- rev - выручка
- visit - количество посетителей
- balance - товарный остаток
- dress_room - количество примерочных
- store_age - возраст магазина в годах
- type - тип магазина
- lch - длина чека = количество покупок / количество чеков
- price - средняя цена покупки = выручка / количество покупок
- visit_per_shift - количество посетителей на смену
Индексирование с помощью []
df1
rev | visit | city | |
---|---|---|---|
shop | |||
sh1 | 140403 | 4546 | city7 |
sh2 | 83276 | 4426 | city8 |
... | ... | ... | ... |
sh82 | 49295 | 1587 | city9 |
sh83 | 49087 | 2017 | city6 |
83 rows × 3 columns
Можно переставить колонки
df1
city | visit | rev | |
---|---|---|---|
shop | |||
sh1 | city7 | 4546 | 140403 |
sh2 | city8 | 4426 | 83276 |
... | ... | ... | ... |
sh82 | city9 | 1587 | 49295 |
sh83 | city6 | 2017 | 49087 |
83 rows × 3 columns
Доступ колонки можно получить через атрибут
df.rev
shop sh1 140403 sh2 83276 ... sh82 49295 sh83 49087 Name: rev, Length: 83, dtype: int64
Однако новую колонку добавить через атрибут не получится, надо через метку
df1['num']=list(range(len(df.index)))
df1
city | visit | rev | num | |
---|---|---|---|---|
shop | ||||
sh1 | city7 | 140403 | 4546 | 0 |
sh2 | city8 | 83276 | 4426 | 1 |
... | ... | ... | ... | ... |
sh82 | city9 | 49295 | 1587 | 81 |
sh83 | city6 | 49087 | 2017 | 82 |
83 rows × 4 columns
Так можно заменить строку
df1
city | visit | rev | num | |
---|---|---|---|---|
shop | ||||
sh1 | city7 | 140403 | 4546 | 0.0 |
sh2 | city10 | 5000 | 100000 | 1.0 |
... | ... | ... | ... | ... |
sh82 | city9 | 49295 | 1587 | 81.0 |
sh83 | city6 | 49087 | 2017 | 82.0 |
83 rows × 4 columns
Срезы по строка, первые пять строк, аналог метода head()
df[:5]
Срезы по строка, последние пять строк, аналог метода tail()
df[-5:]
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh79 | city5 | 31897 | 1172 | 193006 | 4 | 5 | clothing_shop |
sh80 | city8 | 47081 | 1747 | 274833 | 6 | 4 | clothing_shop |
sh81 | city1 | 40197 | 1605 | 240698 | 4 | 4 | dep_store |
sh82 | city9 | 49295 | 1587 | 285435 | 4 | 4 | dep_store |
sh83 | city6 | 49087 | 2017 | 271771 | 6 | 3 | dep_store |
Отбор по метке, метод loc
Отбор по индексу
df.loc['sh05':'sh10']
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh5 | city3 | 67204 | 4048 | 327154 | 8 | 14 | mall |
sh6 | city5 | 70699 | 3729 | 303055 | 8 | 14 | mall |
... | ... | ... | ... | ... | ... | ... | ... |
sh9 | city2 | 73161 | 3404 | 305262 | 8 | 14 | dep_store |
sh10 | city9 | 51893 | 3315 | 266827 | 8 | 14 | dep_store |
6 rows × 7 columns
Можно метки передать списком
df.loc[['sh1','sh25','sh57']]
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh1 | city7 | 140403 | 4546 | 410062 | 8 | 14 | mall |
sh25 | city5 | 76214 | 3486 | 378629 | 8 | 12 | dep_store |
sh57 | city2 | 50080 | 2334 | 255001 | 6 | 8 | clothing_shop |
Отбор строк по индексу и колонок по метке
df.loc[['sh1','sh25','sh57'],['city','rev','visit']]
city | rev | visit | |
---|---|---|---|
shop | |||
sh1 | city7 | 140403 | 4546 |
sh25 | city5 | 76214 | 3486 |
sh57 | city2 | 50080 | 2334 |
Отбор по позиции, метод iloc
Использует целочисленной индексации. Начинается с 0. При срезах включается
начальная граница, верхняя граница не входит. Попытка использовать нецелое число,
даже допустимую метку, вызовет ошибку.
df.iloc[:3,2:]
visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|
shop | |||||
sh01 | 4546 | 410062 | 8 | 14 | mall |
sh02 | 4426 | 363584 | 8 | 14 | mall |
sh03 | 4277 | 221632 | 8 | 14 | mall |
df.iloc[[1,5,15],[2,4]]
visit | dress_room | |
---|---|---|
shop | ||
sh02 | 4426 | 8 |
sh06 | 3729 | 8 |
sh16 | 4029 | 8 |
Случайная выборка метод sample()
Выбираем 5 строк
df.sample(5)
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh37 | city10 | 43819 | 2296 | 242505 | 6 | 11 | clothing_shop |
sh15 | city2 | 34178 | 1169 | 175895 | 4 | 14 | clothing_shop |
sh28 | city4 | 90437 | 4323 | 407493 | 8 | 11 | mall |
sh76 | city10 | 58346 | 2938 | 303361 | 8 | 5 | dep_store |
sh42 | city1 | 68557 | 3492 | 379544 | 8 | 9 | dep_store |
Выбираем 10% строк
df.sample(frac=0.1)
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh34 | city5 | 64006 | 3098 | 318369 | 8 | 11 | dep_store |
sh67 | city8 | 49550 | 2531 | 281340 | 6 | 6 | dep_store |
... | ... | ... | ... | ... | ... | ... | ... |
sh78 | city2 | 51885 | 2101 | 272048 | 6 | 5 | clothing_shop |
sh53 | city8 | 63007 | 3157 | 322412 | 8 | 8 | dep_store |
8 rows × 7 columns
но можно также выполнить выборку с возвратом, задав replace=False.
По умолчанию каждая строка имеет одинаковую вероятность быть выбранной, но если надо, чтобы строки имели разные вероятности быть выбранными , можно передавать
веса. Эти веса могут быть списком, массивом NumPy или серией, но они должны быть той же длины, что и объект, который вы сэмплируете. Пропущенные значения будут
рассматриваться как нулевой вес, и значения inf не допускаются. Если сумма весов не
равна 1, они будут перенормированы путем деления всех весов на сумма весов.
Рассмотрим наш набор данных, допустим мы хотим сдать случайную выборку, но так,
чтобы в ней были представлены все типы магазинов в равных долях. Выведем эти типы, количество магазинов и их доли, им соответствующие.
index=['count','freq'])
dep_store | mall | clothing_shop | |
---|---|---|---|
count | 47.000 | 20.000 | 16.000 |
freq | 0.566 | 0.241 | 0.193 |
посмотрим как в этой выборке распределились магазины по типу
pd.DataFrame([df_smpl_1.type.value_counts(),
df_smpl_1.type.value_counts(normalize=True)],index=['count','freq'])
dep_store | mall | clothing_shop | |
---|---|---|---|
count | 7.0 | 4.0 | 4.0 |
freq | 0.467 | 0.267 | 0.267 |
Если мы хотим равного присутствия типов магазинов, 5 от каждого типа,
то значит вероятность выбора типа должна быть 1/3 деленная на количество
элементов в группе.
Рассчитаем веса для этого
Сначала создадим вспомогательный словарь
keys = list(df.type.value_counts().index)
values = (df.type.value_counts())
kv = list(zip(keys, values))
d = dict(kv)
d
{'dep_store': 47, 'mall': 20, 'clothing_shop': 16}А теперь с помощью лямбда-функции и метода applay добавим в наши данные колонку весов
df['weights']=df.type.apply(lambda x: (1/3)/d.get(x)) df
city | rev | visit | balance | dress_room | store_age | type | weights | |
---|---|---|---|---|---|---|---|---|
shop | ||||||||
sh01 | city7 | 140403 | 4546 | 410062 | 8 | 14 | mall | 0.017 |
sh02 | city8 | 83276 | 4426 | 363584 | 8 | 14 | mall | 0.017 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
sh82 | city9 | 49295 | 1587 | 285435 | 4 | 4 | dep_store | 0.007 |
sh83 | city6 | 49087 | 2017 | 271771 | 6 | 3 | dep_store | 0.007 |
83 rows × 8 columns
Проверим, правильно ли рассчитались веса, у всех трех типов они должны быть 1/3
df.groupby('type')['weights'].sum()
type clothing_shop 0.333 dep_store 0.333 mall 0.333 Name: weights, dtype: float64
Все получилось верно, теперь сделаем выборку с весами и посмотрим на доли
df_smpl_2=df.sample(n=15, weights=df.weights,random_state=2)
pd.DataFrame([df_smpl_2.type.value_counts(),
df_smpl_2.type.value_counts(normalize=True)],index=['count','freq'])
dep_store | clothing_shop | mall | |
---|---|---|---|
count | 6.0 | 5.000 | 4.000 |
freq | 0.4 | 0.333 | 0.267 |
Не совсем то, что хотелось, но все же ближе к цели, по крайней мере влияние
весов заметили
Рассмотрим случайные выборки из еще одного набора данных
df_day = pd.read_excel("mydata/visit_rev_day.xlsx",index_col="date")
df_day
day_on_off | visit | rev | |
---|---|---|---|
date | |||
2019-10-01 | 0 | 148 | 1138 |
2019-10-02 | 0 | 159 | 1119 |
... | ... | ... | ... |
2019-10-30 | 0 | 162 | 1063 |
2019-10-31 | 0 | 142 | 1231 |
31 rows × 3 columns
Обозначения :
- data - дата торгового дня
- day_on_off - день недели, 0 - рабочий, 1 - выходной
- rev - выручка
- visit - количество посетителей
Допустим мы хотим сделать случайную выборку семи дней и рассчитать по ней средние показатели посетителей и выручки и сравнить их с истинными средними
Первая выборка
pd.DataFrame([df_smpl_1.day_on_off.value_counts(),
df_smpl_1.day_on_off.value_counts(normalize=True)],index=['count','freq'])
0 | 1 | |
---|---|---|
count | 5.000 | 2.000 |
freq | 0.714 | 0.286 |
В нее попали пять рабочих и два выходных, логично.
Для второй выборки в качестве веса используем количество посетителей, помним,
что метод сам нормирует эти веса.
df_smpl_2=df_day.sample(n=7,weights=df_day.visit,random_state=2)
pd.DataFrame([df_smpl_2.day_on_off.value_counts(),
df_smpl_2.day_on_off.value_counts(normalize=True)],index=['count','freq'])
0 | 1 | |
---|---|---|
count | 4.000 | 3.000 |
freq | 0.571 | 0.429 |
Выборочная неделя получилось "праздничной", с тремя выходными, для расчета
среднемесячных показателей не подойдет.
В третьей выборке в качестве весов используем дневные выручки
df_smpl_3=df_day.sample(n=7,weights=df_day.rev,random_state=2)
pd.DataFrame([df_smpl_3.day_on_off.value_counts(),
df_smpl_3.day_on_off.value_counts(normalize=True)],index=['count','freq'])
1 | 0 | |
---|---|---|
count | 4.000 | 3.000 |
freq | 0.571 | 0.429 |
Получилась еще более "праздничная" неделя четырьмя выходными.
Четвертую выборку сделаем с заранее заданным количеством рабочих и выходных,
отдельно выберем пять рабочих дней и два выходных.
df_smpl_4=pd.concat([df_day[df_day['day_on_off']==0].sample(n=5,random_state=2),
df_day[df_day['day_on_off']==1].sample(n=2,random_state=2)])
df_smpl_4
day_on_off | visit | rev | |
---|---|---|---|
date | |||
2019-10-29 | 0 | 136 | 1150 |
2019-10-01 | 0 | 148 | 1138 |
... | ... | ... | ... |
2019-10-19 | 1 | 430 | 3110 |
2019-10-06 | 1 | 415 | 2819 |
7 rows × 3 columns
Теперь посмотрим, что у нас получилось со средними выборочными показателями и сравним их с истинными средними
Отдельно сделаем набор с количеством рабочих и выходных
df_smpl_1.day_on_off.value_counts(),
df_smpl_2.day_on_off.value_counts(),
df_smpl_3.day_on_off.value_counts(),
df_smpl_4.day_on_off.value_counts()],
index=['main','samp1','samp2','samp3','samp4'])
df_01
0 | 1 | |
---|---|---|
main | 23 | 8 |
samp1 | 5 | 2 |
samp2 | 4 | 3 |
samp3 | 3 | 4 |
samp4 | 5 | 2 |
И набор со средними
df_02=pd.DataFrame([df_day.iloc[:,[1,2]].describe().mean(), df_smpl_1.iloc[:,[1,2]].describe().mean(), df_smpl_2.iloc[:,[1,2]].describe().mean(), df_smpl_3.iloc[:,[1,2]].describe().mean(), df_smpl_4.iloc[:,[1,2]].describe().mean(), ],index=['main','samp1','samp2','samp3','samp4']) df_02
visit | rev | |
---|---|---|
main | 185.265 | 1336.159 |
samp1 | 184.713 | 1335.479 |
samp2 | 212.195 | 1500.340 |
samp3 | 249.584 | 1747.994 |
samp4 | 187.918 | 1367.704 |
И объединим их в один набор
pd.concat([df_01,df_02],axis=1)
0 | 1 | visit | rev | |
---|---|---|---|---|
main | 23 | 8 | 185.265 | 1336.159 |
samp1 | 5 | 2 | 184.713 | 1335.479 |
samp2 | 4 | 3 | 212.195 | 1500.340 |
samp3 | 3 | 4 | 249.584 | 1747.994 |
samp4 | 5 | 2 | 187.918 | 1367.704 |
Как видно, наиболее близкие значения получили из выборки без весов и без
предварительного разделения на рабочие и выходные
Логическое индексирование
Отбираем магазины старше 3 и моложе 10 лет
df[(df.store_age>3)&(df.store_age<10)]
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh42 | city1 | 68557 | 3492 | 379544 | 8 | 9 | dep_store |
sh43 | city5 | 84720 | 3169 | 423576 | 8 | 9 | dep_store |
... | ... | ... | ... | ... | ... | ... | ... |
sh81 | city1 | 40197 | 1605 | 240698 | 4 | 4 | dep_store |
sh82 | city9 | 49295 | 1587 | 285435 | 4 | 4 | dep_store |
41 rows × 7 columns
Убираем магазины старше 5 лет
df[~(df.store_age>5)]
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh70 | city9 | 80977 | 3588 | 333327 | 8 | 5 | mall |
sh71 | city9 | 83704 | 3458 | 344648 | 8 | 5 | dep_store |
... | ... | ... | ... | ... | ... | ... | ... |
sh82 | city9 | 49295 | 1587 | 285435 | 4 | 4 | dep_store |
sh83 | city6 | 49087 | 2017 | 271771 | 6 | 3 | dep_store |
14 rows × 7 columns
индекс которых заканчивается на 5-ку
df[criterion]
city | rev | visit | balance | dress_room | store_age | type | |
---|---|---|---|---|---|---|---|
shop | |||||||
sh05 | city3 | 67204 | 4048 | 327154 | 8 | 14 | mall |
sh15 | city2 | 34178 | 1169 | 175895 | 4 | 14 | clothing_shop |
... | ... | ... | ... | ... | ... | ... | ... |
sh65 | city10 | 59680 | 2907 | 265505 | 8 | 6 | dep_store |
sh75 | city8 | 57386 | 3091 | 305580 | 8 | 5 | dep_store |
8 rows × 7 columns
Эквивалентный отбор, но работает медленнее
df[[x.endswith('5') for x in df.index]]
Индексирование с помощью метода isin()
столбцов имеют нужные вам значения: Тот же метод доступен для объектов Index и
полезен в тех случаях, когда вы не знаете, какие из меток присутствуют:
df[df.index.isin(['sh01', 'sh56', 'sh94'])]
дневные продажи по чекам розничного магазина
df0318
date | year | month | shop | num | sum | amt | |
---|---|---|---|---|---|---|---|
0 | 2018-03-01 | 2018 | 3 | Sh2 | ch2018311 | 260 | 2 |
1 | 2018-03-01 | 2018 | 3 | Sh2 | ch2018312 | 310 | 2 |
... | ... | ... | ... | ... | ... | ... | ... |
1114 | 2018-03-31 | 2018 | 3 | Sh2 | ch201833164 | 596 | 4 |
1115 | 2018-03-31 | 2018 | 3 | Sh2 | ch201833165 | 124 | 2 |
1116 rows × 7 columns
Обозначения :
- date - дата чека
- year - год
- month - месяц
- shop - индекс магазина
- num - номер чека
- sum - сумма чека
- amt - количество вещей в чеке
Сначала создаем набор уникальных дат
df_date=df0318.date.unique()
повторов пяти дат
np.random.seed(1985) rand_date=np.random.choice(df_date, size=5, replace=False) rand_date
array(['2018-03-10T00:00:00.000000000', '2018-03-30T00:00:00.000000000', '2018-03-05T00:00:00.000000000', '2018-03-24T00:00:00.000000000', '2018-03-29T00:00:00.000000000'], dtype='datetime64[ns]')
И с помощью метода isin() отбираем соответствующими им строки с чеками
df5
date | year | month | shop | num | sum | amt | |
---|---|---|---|---|---|---|---|
175 | 2018-03-05 | 2018 | 3 | Sh2 | ch2018351 | 520 | 3 |
176 | 2018-03-05 | 2018 | 3 | Sh2 | ch2018352 | 508 | 3 |
... | ... | ... | ... | ... | ... | ... | ... |
1049 | 2018-03-30 | 2018 | 3 | Sh2 | ch201833023 | 200 | 3 |
1050 | 2018-03-30 | 2018 | 3 | Sh2 | ch201833024 | 152 | 3 |
196 rows × 7 columns
Рассчитываем средний чек по всему набору и по выборке
print(round(df0318.loc[:,'sum'].mean(),0),round(df5.loc[:,'sum'].mean(),0))
333.0 331.0
Значения очень близки, то есть по такой случайной выборке мы можем представить
значение среднего чека по всей совокупности
Метод query()
Метод позволяет выбирать с помощью выражения, допустимы следующие варианты :
df.query('dress_room > 4 & store_age < 5')
df.query('dress_room > 4 and store_age < 5')
df.query('type == "mall"')
Методы numpy : where и select
Заменяем строковый тип магазинов на числовой
df['type_num']=np.where(df['type']=='mall',1,np.where(df['type']=='dep_store',2,3))
city rev visit balance dress_room store_age type type_num shop sh01 city7 140403 4546 410062 8 14 mall 1 sh02 city8 83276 4426 363584 8 14 mall 1 ... ... ... ... ... ... ... ... ... sh82 city9 49295 1587 285435 4 4 dep_store 2 sh83 city6 49087 2017 271771 6 3 dep_store 2
83 rows × 8 columns
Вариант с методом select()
conditions = [(df['type'] == 'mall'),(df['type'] == 'dep_store'),
(df['type'] == 'clothing_shop')]
choices= [1,2,3]
df['type_num_2'] = np.select(conditions, choices, default=0)
city | rev | visit | balance | dress_room | store_age | type | type_num | type_num_2 | |
---|---|---|---|---|---|---|---|---|---|
shop | |||||||||
sh01 | city7 | 140403 | 4546 | 410062 | 8 | 14 | mall | 1 | 1 |
sh02 | city8 | 83276 | 4426 | 363584 | 8 | 14 | mall | 1 | 1 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
sh82 | city9 | 49295 | 1587 | 285435 | 4 | 4 | dep_store | 2 | 2 |
sh83 | city6 | 49087 | 2017 | 271771 | 6 | 3 | dep_store | 2 | 2 |
83 rows × 9 columns
Комментариев нет:
Отправить комментарий