Разпределения
Накратко: Разпределението показва как са пръснати всички стойности наведнъж: хистограмата и кутията с мустаци го показват за една величина, а диаграмата на разсейване връзката между две.
Проблем
„Колко струва една поръчка?“
Управителят иска да знае колко струва една поръчка. Сметнал е средната: 10,63 €. Ще планира рекламата за „типичния клиент с поръчка около 10 €“.
Продадените поръчки (без отказаните) са 10 459, а 6 736 от тях са под средната. Почти две от всеки три. Как така „средното“ е над повечето? Отговорът се вижда, като погледнеш всички поръчки наведнъж, а не едно число.
Идеята
Хистограма
Как се пръскат стойностите, кои са чести и кои редки, се казва разпределение. Картината му е хистограмата: оста x се дели на равни интервали, а всеки стълб е колко поръчки попадат в своя интервал.
ax.hist(values, bins=30) прави 30 интервала от най-малката до най-голямата стойност и ги брои. Връща три неща: броя във всеки интервал, границите и самите стълбове.
Първият стълб е от 0,80 до 3,40 € и в него има 1 915 поръчки. Вторият, от 3,40 до 5,99 €, е най-високият: 2 122. Нататък стълбовете падат, а вдясно остава тънка ивица от редки поръчки чак до 78,70 €. Тази ивица се казва опашка. Разпределение с дълга опашка надясно е обичайно за пари: много малки покупки и малко големи.
Идеята
Броят стълбове мени картината
bins решаваш ти, а различният брой разказва различно. Ето същите поръчки с 4 и с 80 стълба:
С 4 стълба виждаш само, че 9 091 поръчки са под 20,28 €, 1 182 са до 39,75 €, а след това почти няма. С 80 стълба се вижда, че най-много поръчки са между 2 и 8 €, а броят пада плавно към опашката. Зъбците (един стълб висок, съседът нисък) идват от цените: някои суми се събират по-често от други.
Няма един верен брой. Пробвай няколко и избери този, при който формата се вижда, а шумът не я закрива. За няколко хиляди стойности 30–60 стълба обикновено е добро начало.
По-точно, за любопитните
bins може да е и списък с границите: bins=[0, 5, 10, 20, 40, 80]. Тогава стълбовете не са равни по ширина и височината подвежда: широкият стълб събира повече поръчки само защото е широк. С density=True височината се дели на ширината на стълба и широките стълбове вече не подвеждат.
Пробвай
Декември отблизо
Как изглеждат поръчките през декември? Запиши в december продадените поръчки от декември и нарисувай хистограма на стойността им с 40 стълба. Надпиши оста x.
Решение
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv', parse_dates=['ordered_at'])
items = pd.read_csv('year_items.csv')
items['revenue'] = items['quantity'] * items['unit_price']
per_order = items.groupby('order_id', as_index=False).agg(
revenue=('revenue', 'sum'), lines=('product_id', 'size'), pieces=('quantity', 'sum'))
orders = orders.merge(per_order, left_on='id', right_on='order_id', how='left', validate='one_to_one')
# Оборотът е без отказаните поръчки.
sold = orders[orders['status'] != 'cancelled']
values = sold['revenue']
december = sold[sold['ordered_at'].dt.month == 12]
fig, ax = plt.subplots()
ax.hist(december['revenue'], bins=40)
ax.set_title('Поръчките през декември')
ax.set_xlabel('Стойност на поръчката, €')
ax.set_ylabel('Брой поръчки')sold[sold["ordered_at"].dt.month == 12] дава 1 211 поръчки, а ax.hist(december["revenue"], bins=40) ги разпределя от 0,80 до 78,00 €. Освен върха вляво се вижда втора гърбица около 20–30 €: в много от тези поръчки има подаръчна карта за 20 €, а тя се продава най-вече през декември. Без bins matplotlib слага 10 стълба и формата се губи. Ако вземеш поръчките от orders, в хистограмата влизат и 74 отказани: 1 285 вместо 1 211.
Идеята
Средна и медиана
Подреди всички поръчки от най-малката до най-голямата. Стойността точно по средата е медианата: половината поръчки са под нея, половината над нея. values.median() я дава.
Начертай средната и медианата върху хистограмата като вертикални линии. ax.axvline(x) рисува линия през цялата височина на графиката в точката x:
Средната е 10,63 €, а медианата е 7,70 €. Медианата е там, където е тежестта на стълбовете. Средната е вдясно от нея: опашката от големи поръчки я дърпа нагоре, защото всяко евро от тях влиза в сбора. На медианата ѝ е все едно дали най-голямата поръчка е 78 € или 7 800 €.
Затова при пари, времена и размери, където има опашка, гледай и двете. Ако са далеч една от друга, медианата е по-близо до „типичния клиент“.
Идеята
Кутия с мустаци
Хистограмата е подробна, но две или пет хистограми една до друга се сравняват трудно. Кутията с мустаци (box plot) свива разпределението до пет неща. Декември е най-силният месец: поръчките по-големи ли са, или само са повече?
Как се чете:
- линията в кутията е медианата: 8,60 € през декември и 7,60 € през другите месеци;
- кутията е средната половина от поръчките: четвърт са под нея, четвърт са над нея;
- мустаците стигат до последните обичайни стойности;
- кръгчетата са необичайно големите поръчки.
Декемврийската кутия е по-висока и стига по-нагоре: там има повече големи поръчки (подаръци, тигани, ножове). Затова средната се мени повече от медианата: 13,14 € срещу 10,30 €, а медианата е 8,60 € срещу 7,60 €. Декември има и повече поръчки, и по-големи, но разликата е най-вече в опашката.
По-точно, за любопитните
Мустакът стига до най-далечната поръчка, която е на не повече от 1,5 височини на кутията от нея. Всичко по-далеч е кръгче. Това правило и височината на кутията (IQR) ще смяташ с числа в урока за описанието на данните.
В по-старите версии на matplotlib (преди 3.9) етикетите се дават с labels= вместо tick_labels=.
Идеята
Разсейване: две числа на всяка поръчка
Растат ли парите с броя на редовете в поръчката? Тук имаш две числа на поръчка, а въпросът е за връзката им. Картината е диаграма на разсейване (scatter plot): всяка поръчка е точка, x е едното число, y е другото. ax.scatter(x, y) я рисува.
Отляво всичко е в плътни колони. Хиляди точки са една върху друга и не виждаш къде са много и къде са малко. Това се казва припокриване (overplotting). Отдясно alpha=0.03 прави всяка точка почти прозрачна: където са много, цветът се натрупва и е тъмно.
Сега се вижда, че с всеки ред поръчката расте средно с около 4–5 €: от 4,27 € при един ред (2 517 поръчки) до 27,48 € при шест (145 поръчки). Но точките на всеки ред са разпръснати нагоре: един скъп продукт струва колкото десет евтини.
sharey=True дава на двете графики една и съща ос y, за да се сравняват честно.
Пробвай
Бройки и стойност
Колоната pieces е броят бройки в поръчката (сборът на quantity). Нарисувай диаграма на разсейване: бройките по x, стойността на поръчката по y, с прозрачност, за да се вижда къде са много поръчки. Надпиши двете оси.
Решение
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv', parse_dates=['ordered_at'])
items = pd.read_csv('year_items.csv')
items['revenue'] = items['quantity'] * items['unit_price']
per_order = items.groupby('order_id', as_index=False).agg(
revenue=('revenue', 'sum'), lines=('product_id', 'size'), pieces=('quantity', 'sum'))
orders = orders.merge(per_order, left_on='id', right_on='order_id', how='left', validate='one_to_one')
# Оборотът е без отказаните поръчки.
sold = orders[orders['status'] != 'cancelled']
values = sold['revenue']
fig, ax = plt.subplots()
ax.scatter(sold['pieces'], sold['revenue'], alpha=0.05)
ax.set_title('Бройки и стойност на поръчката')
ax.set_xlabel('Бройки в поръчката')
ax.set_ylabel('Стойност на поръчката, €')ax.scatter(sold["pieces"], sold["revenue"], alpha=0.05) рисува 10 459 точки. Бройките са от 1 до 14, а точките са най-гъсти между 1 и 5 бройки и под 20 €. Без alpha колоните са плътни и поръчка с 14 бройки изглежда толкова важна, колкото хилядите с 2.
Упражнение
Отговорът за управителя
Управителят иска една графика: колко струват поръчките и къде са средната и медианата. Запиши в mean_value средната, а в median_value медианата на стойността на продадените поръчки. Нарисувай хистограма с 50 стълба и двете стойности като вертикални линии, с легенда.
Решение
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv', parse_dates=['ordered_at'])
items = pd.read_csv('year_items.csv')
items['revenue'] = items['quantity'] * items['unit_price']
per_order = items.groupby('order_id', as_index=False).agg(
revenue=('revenue', 'sum'), lines=('product_id', 'size'), pieces=('quantity', 'sum'))
orders = orders.merge(per_order, left_on='id', right_on='order_id', how='left', validate='one_to_one')
# Оборотът е без отказаните поръчки.
sold = orders[orders['status'] != 'cancelled']
values = sold['revenue']
mean_value = values.mean()
median_value = values.median()
fig, ax = plt.subplots()
ax.hist(values, bins=50, color='lightgray')
ax.axvline(mean_value, color='black', label='средна')
ax.axvline(median_value, color='black', linestyle='--', label='медиана')
ax.set_title('Колко струва една поръчка')
ax.set_xlabel('Стойност на поръчката, €')
ax.legend()
print(round(mean_value, 2), round(median_value, 2))Средната е 10,63 €, а медианата е 7,70 €. Половината поръчки са под 7,70 €, а 6 736 от 10 459 са под средната. Типичната поръчка е по-близо до 7,70 €, а средната е по-висока заради опашката от големи поръчки до 78,70 €. Ако смяташ от orders, в числата влизат и отказаните: средната излиза 10,61 €.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Групи: groupby · Съединяване: merge · Първата графика · Коя графика за какъв въпрос
- Продължава в: Описание на данните · Извадка и популация · Доверителен интервал · Корелация и причина
- Задачи по темата: Графики