# pandas 라이브러리를 불러옵니다. pd를 약칭으로 사용합니다.
import pandas as pd
import numpy as np
DataFrame은 2차원 테이블이고, 테이블의 한 줄(행/열)을 Series라고 합니다.
Series의 모임이 곧, DataFrame이 됩니다.
# 12x4 행렬에 1부터 36까지의 숫자를 원소를 가지고, index는 0부터 시작하고, coulmns은 순서대로 X1, X2, X3, X4로 하는 DataFrame 생성
np.random.seed(42)
df = pd.DataFrame(data=np.random.randn(10, 3),
columns=['X1', 'X2', 'X3'],
index=pd.date_range('2024-01-01', '2024-01-10'))
df
# dataframe index
df.index
>>> DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04',
'2024-01-05', '2024-01-06', '2024-01-07', '2024-01-08',
'2024-01-09', '2024-01-10'],
dtype='datetime64[ns]', freq='D')
# dataframe columns
df.columns
>>> Index(['X1', 'X2', 'X3'], dtype='object')
# dataframe values
df.values
>>> array([[ 0.49671415, -0.1382643 , 0.64768854],
[ 1.52302986, -0.23415337, -0.23413696],
[ 1.57921282, 0.76743473, -0.46947439],
[ 0.54256004, -0.46341769, -0.46572975],
[ 0.24196227, -1.91328024, -1.72491783],
[-0.56228753, -1.01283112, 0.31424733],
[-0.90802408, -1.4123037 , 1.46564877],
[-0.2257763 , 0.0675282 , -1.42474819],
[-0.54438272, 0.11092259, -1.15099358],
[ 0.37569802, -0.60063869, -0.29169375]])
# 특정 column을 가져오기
df['X2']
>>> 2024-01-01 -0.138264
2024-01-02 -0.234153
2024-01-03 0.767435
2024-01-04 -0.463418
2024-01-05 -1.913280
2024-01-06 -1.012831
2024-01-07 -1.412304
2024-01-08 0.067528
2024-01-09 0.110923
2024-01-10 -0.600639
Freq: D, Name: X2, dtype: float64
# X1 column에 2 더하기
df['X1'] ** 2
>>> 2024-01-01 0.246725
2024-01-02 2.319620
2024-01-03 2.493913
2024-01-04 0.294371
2024-01-05 0.058546
2024-01-06 0.316167
2024-01-07 0.824508
2024-01-08 0.050975
2024-01-09 0.296353
2024-01-10 0.141149
Freq: D, Name: X1, dtype: float64
# dataframe의 맨 위 다섯줄을 보여주는 head()
df.head(3)
# dataframe에 대한 전체적인 요약정보를 보여줍니다. index, columns, null/not-null/dtype/memory usage가 표시됩니다.
df.info()
>>> <class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 10 entries, 2024-01-01 to 2024-01-10
Freq: D
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 X1 10 non-null float64
1 X2 10 non-null float64
2 X3 10 non-null float64
dtypes: float64(3)
memory usage: 320.0 bytes
# dataframe에 대한 전체적인 통계정보를 보여줍니다.
df.describe()
# X2 column를 기준으로 내림차순 정렬
df.sort_values(by='X2', ascending=False)
SQL Join(inner, left, outer)
<=> pd.merge
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index=[0, 1, 2, 3])
df2 = pd.DataFrame({'A': ['A2', 'A3', 'A4', 'A5'],
'E': ['B4', 'B5', 'B6', 'B7'],
'F': ['C4', 'C5', 'C6', 'C7'],
'G': ['D4', 'D5', 'D6', 'D7']},
index=[0, 1, 2, 3])
df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
'B': ['B8', 'B9', 'B10', 'B11'],
'C': ['C8', 'C9', 'C10', 'C11'],
'D': ['D8', 'D9', 'D10', 'D11']},
index=[8, 9, 10, 11])
![]() | ![]() |
---|
# SQL과 같이 join operation을 사용할 수 있다.
# inner : 교집합.
pd.merge(df1, df2, how='inner', on='A')
# left join
# left : 왼쪽 데이터를 기준으로 있는 것들만 합치고, 없는건 결측치로.
pd.merge(df1, df2, how='left', on="A")
#pd.merge(df1, df2, how='outer', on="A")
# 그냥 합치기 (concatenation)
pd.concat([df1, df2, df3])
Source : https://www.kaggle.com/c/titanic/data
# train.csv 파일 불러오기
titanic = pd.read_csv('./data/train.csv')
titanic
# Q1. 생존자 비율
titanic.Survived.value_counts(normalize=True)
>>> 0 0.616162
1 0.383838
Name: Survived, dtype: float64
# Q2. 여성 승객들의 평균 나이.
female = titanic[titanic.Sex == 'female'] # boolean mask(=filter) --> fancy indexing
np.mean(female.Age) # female.Age.mean()
# DataFrame의 특정 조건에 해당하는 데이터를 뽑는 방법.
# .loc[row에 대한 조건, col에 대한 조건]
female_age = titanic.loc[titanic.Sex == 'female', 'Age']
np.mean(female_age)
>>> 1 38.0
2 26.0
3 35.0
8 27.0
9 14.0
...
880 25.0
882 22.0
885 39.0
887 19.0
888 NaN
Name: Age, Length: 314, dtype: float64
# Q3. 남성 승객들중에서 가장 높은 fare.
male_fare = titanic.loc[titanic.Sex == 'male', 'Fare']
np.max(male_fare)
>>> 0 7.2500
4 8.0500
5 8.4583
6 51.8625
7 21.0750
...
883 10.5000
884 7.0500
886 13.0000
889 30.0000
890 7.7500
Name: Fare, Length: 577, dtype: float64
# Q4. 이름에 Mrs.가 포함된 사람 찾기 (= 기혼 여성이 몇명인지 찾기)
titanic[titanic.Name.str.contains('Mrs')]
# Q5. 결측치(NaN)가 포함된 모든 데이터 찾기
titanic[titanic.isnull().any(axis=1)]
# Q6. 해당 column의 결측치가 50% 이상인 column을 찾아서 drop하기
drop_cols = titanic.columns[titanic.isnull().mean() >= 0.5] # 각 column별 결측치 비율이 50% 이상인 column.
titanic = titanic.drop(columns=drop_cols) # overwrite
titanic
pivot table이란 기존 테이블 구조를 특정 column을 기준으로 재구조화한 테이블을 말합니다.
특정 column을 기준으로 pivot하기 때문에, 어떤 column에 어떤 연산을 하느냐에 따라서 만들어지는 결과가 바뀝니다.
주로 어떤 column을 기준으로 데이터를 해석하고 싶을 때 사용합니다.
# 성별을 기준으로 생존률 파악 --> Mean vs Sum
pd.pivot_table(data=titanic, index=['Sex'], values=['Survived'], aggfunc='mean')
# 사회 계급을 기준으로 생존률 파악
pd.pivot_table(data=titanic, index=['Sex', 'Pclass'], values=['Survived'], aggfunc=['count', 'sum', 'mean'])
numpy의 기본적인 사용법에 대해서 배워봅니다.
numpy에서 numpy.array를 만드는 여러가지 방법과 지원하는 연산자에 대해서 공부합니다.
# numpy 라이브러리를 불러옵니다.
import numpy as np
# 파이썬 리스트 선언
data = [1, 2, 3, 4, 5]
data
>>> [1, 2, 3, 4, 5]
# 파이썬 2차원 리스트(행렬) 선언
data2 = [ [1, 2], [3, 4] ]
data2
>>> [[1, 2], [3, 4]]
np.array는 생성된 이후에 크기 변경이 불가능
np.array는 "무조건" 모든 원소의 dtype이 동일해야 함
numpy는 위의 세팅을 만족하는 것으로 인해서, 사용할 연산자와 연산 횟수가 고정되는 이점이 있다!
# 파이썬 list를 numpy array로 변환합니다.
arr = np.array(data)
# numpy array를 만드는a 방식의 대부분은 파이썬 리스트를 np.array로 변환하는 방식입니다.
arr
>>> array([1, 2, 3, 4, 5])
# 2차원 리스트를 np.array로 만듭니다.
arr2 = np.array(data2) # data2라는 리스트를 numpy array로 만들어라.
arr2
>>> array([[1, 2],
[3, 4]])
# v1 = (1, 2, 3), v2 = (4, 5, 6) 벡터 2개 생성하기.
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
# 리스트로 더하기 연산해보기 ---> concatenation
[n1 + n2 for n1, n2 in zip([1, 2, 3], [4, 5, 6])]
>>> [5, 7, 9]
# vector addition
v1 + v2
>>> array([5, 7, 9])
# vector subtraction
v1 - v2
>>> array([-3, -3, -3])
# (not vector operation) elementwise multiplication
v1 * v2
>>> array([ 4, 10, 18])
# (not vector operation) elementwise division
v1 / v2
>>> array([0.25, 0.4 , 0.5 ])
# dot product : 같은 위치에 있는 원소들을 각자 곱하고, 모두 더한 결과. (내적)
v1 @ v2
>>> 32
numpy에서 사용되는 여러가지 함수들을 사용해봅시다.
# 표준정규분포에서 random sampling을 한 원소를 가지는 5x3 행렬을 만든다.
np.random.seed(42) ## seed number
mat1 = np.random.randn(5, 3)
mat1
>>> array([[ 0.49671415, -0.1382643 , 0.64768854],
[ 1.52302986, -0.23415337, -0.23413696],
[ 1.57921282, 0.76743473, -0.46947439],
[ 0.54256004, -0.46341769, -0.46572975],
[ 0.24196227, -1.91328024, -1.72491783]])
# mat1에 절대값 씌우기
np.abs(mat1)
>>> array([[0.49671415, 0.1382643 , 0.64768854],
[1.52302986, 0.23415337, 0.23413696],
[1.57921282, 0.76743473, 0.46947439],
[0.54256004, 0.46341769, 0.46572975],
[0.24196227, 1.91328024, 1.72491783]])
# mat1 제곱하기
np.square(mat1)
>>> array([[0.24672495, 0.01911702, 0.41950044],
[2.31961994, 0.0548278 , 0.05482011],
[2.49391312, 0.58895606, 0.2204062 ],
[0.2943714 , 0.21475596, 0.2169042 ],
[0.05854574, 3.66064129, 2.97534153]])
# mat1의 제곱근 구하기
np.sqrt(mat1)
>>> array([[0.70477951, nan, 0.80479099],
[1.23411096, nan, nan],
[1.25666734, 0.87603352, nan],
[0.73658675, nan, nan],
[0.49189661, nan, nan]])
# linear algebra functions
vec = np.array([1, 0, 1])
# 1. norm
np.linalg.norm(vec) # sqrt2
# 2. eigenvalue
np.linalg.eig([[1, 0], [0, 1]])
>>> (array([1., 1.]),
array([[1., 0.],
[0., 1.]]))
np.random.seed(0xC0FFEE)
mat2 = np.random.rand(3, 2)
mat2
>>> array([[0.57290783, 0.81519505],
[0.92585076, 0.09358959],
[0.26716135, 0.96059676]])
# Summation
# axis=0 : column
# axis=1 : row
np.sum(mat2, axis=1)
>>> array([1.38810288, 1.01944035, 1.22775812])
# mean
np.mean(mat2)
>>> 0.6058835588121987
# std
np.std(mat2)
>>> 0.3292104202289578
mat2
>>> array([[0.57290783, 0.81519505],
[0.92585076, 0.09358959],
[0.26716135, 0.96059676]])
# min, max
#np.min(mat2, axis=0)
np.max(mat2, axis=1)
>>> array([0.81519505, 0.92585076, 0.96059676])
# 최소값이 있는 Index
np.argmin(mat2, axis=0)
>>> array([2, 1])
# 최대값이 있는 Index
np.argmax(mat2, axis=1)
>>> array([1, 0, 1])
Universal Function 기능을 통해 반복문을 사용한 것보다 훨씬 빠른 성능을 냅니다.
직접 실험을 통해 그 차이를 확인해보겠습니다.
np.random.seed(0)
def reverse_num(values):
output = np.empty(len(values))
for i in range(len(values)):
output[i] = 1.0 / values[i]
return output
# 1부터 100까지 범위에서 1000000개를 랜덤으로 뽑아서 array를 만듭니다.
big_array = np.random.randint(1, 101, 10000000)
%timeit reverse_num(big_array)
>>> 6.71 s ± 28.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit 1.0 / big_array
>>> 10.4 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# euclidean distance
def euclidean_distance(x, y):
# x, y : np.array (n-dim vector)
return np.sqrt(np.sum(np.square(x - y)))
# cosine distance
def cosine_distance(x, y):
## TO-DO ##
# 주어진 x, y에 대해서 x, y 사이의 cosine distance를 계산해보세요.
# 이론상의 최소값은 0, 최대값은 2입니다.
## cosine distance는 1 - cosine similarity로 정의됩니다.
n1 = np.sqrt(np.sum(np.square(x)))
n2 = np.sqrt(np.sum(np.square(y)))
cosine_sim = (x @ y) / (n1*n2)
return 1 - cosine_sim
v1 = np.array([1, 2, 3, -4])
v2 = np.array([-1, 2, 5, 1])
euclidean_distance(v1, v2), cosine_distance(v1, v2)
>>> (5.744562646538029, 0.5409219149512329)
엄청나게 화려한 시각화 기법들을 제공하며, 기본적으로 이쁩니다.
histplot, barplot, jointplot, lineplot, ...
pandas DataFrame과 매우 호환이 잘 됩니다.
e.g. sns.xxxplot(data=df) <--- 기본세팅!
# 라이브러리와 데이터를 불러오고, 시각화를 위한 세팅을 합니다.
import seaborn as sns
data = sns.load_dataset('penguins')
data = data.dropna()
data
가장 기본적으로 사용되는 히스토그램을 출력하는 plot.
전체 데이터를 특정 구간별 정보를 확인할 때 사용합니다.
# penguin 데이터에 histplot을 출력합니다.
sns.histplot(data=data, x='body_mass_g',
hue='species', multiple='stack', palette='Set2',
bins=30)
distribution들을 여러 subplot들로 나눠서 출력해주는 plot.
displot에 kind를 변경하는 것으로, histplot, kdeplot, ecdfplot 모두 출력이 가능합니다.
# penguin 데이터에 displot을 출력합니다.
sns.displot(data=data, x='bill_length_mm',
kind='hist', col='sex', row='island',
hue='species')
어떤 데이터에 대한 값의 크기를 막대로 보여주는 plot. (a.k.a. 막대그래프)
가로 / 세로 두 가지로 모두 출력 가능합니다.
히스토그램과는 다릅니다!
# penguin 데이터에 barplot을 출력합니다.
sns.barplot(data=data, y='species', x='body_mass_g', hue='sex',
errorbar=None)
범주형 속성을 가지는 데이터들의 histogram을 보여주는 plot.
종류별 count를 보여주는 방법입니다.
# penguin 데이터에 countplot을 출력합니다.
sns.countplot(data=data, x='island')
데이터의 각 종류별로 사분위 수(quantile)를 표시하는 plot.
특정 데이터의 전체적인 분포를 확인하기 좋은 시각화 기법입니다.
box와 전체 range의 그림을 통해 outlier를 찾기 쉽습니다. (IQR : Inter-Quartile Range)
# penguin 데이터에 boxplot을 출력합니다.
sns.boxplot(data=data[['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm']])
# What is boxplot?
데이터에 대한 분포 자체를 보여주는 plot.
boxplot과 비슷하지만, 전체 분포에 대한 그림을 보여준다는 점에서 boxplot과 다릅니다.
보통 boxplot과 함께 표시하면, 평균 근처에 데이터가 얼마나 있는지(boxplot) 전체적으로 어떻게 퍼져있는지(violinplot) 모두 확인이 가능합니다.
# penguin 데이터에 violinplot을 출력합니다.
sns.violinplot(data=data, x='species', y='body_mass_g')
특정 데이터를 x, y로 표시하여 관계를 확인할 수 있는 plot. (선 그래프)
수치형 지표들 간의 경향을 파악할 때 많이 사용합니다.
# penguin 데이터에 lineplot을 출력합니다.
# sns.lineplot(data=data, x='body_mass_g', y='flipper_length_mm', errorbar=None,
# hue='species')
sns.lineplot(x=data['body_mass_g'], y=data['flipper_length_mm'], errorbar=None)
특정 수치 데이터를 error bar와 함께 출력해주는 plot.
수치 데이터를 다양한 각도에서 한 번에 바라보고 싶을 때 사용합니다.
데이터와 error bar를 한 번에 찍어주기 때문에, 살펴보고 싶은 특정 지표들만 사용하는 것이 좋습니다.
# penguin 데이터에 pointplot을 출력합니다.
sns.pointplot(data=data, x='species', y='flipper_length_mm')
lineplot과 비슷하게 x, y에 대한 전체적인 분포를 확인하는 plot.
lineplot은 경향성에 초점을 둔다면, scatterplot은 데이터 그 자체가 퍼져있는 모양에 중점을 둡니다.
# penguin 데이터에 scatterplot을 출력합니다.
sns.scatterplot(data=data, x='bill_depth_mm', y='bill_length_mm')
주어진 데이터의 각 feature들 사이의 관계를 표시하는 Plot.
scatterplot, FacetGrid, kdeplot을 이용하여 feature간의 관계를 잘 보여줍니다.
각 feature에 대해 계산된 모든 결과를 보여주기 때문에, feature가 많은 경우 사용하기 적합하지 않습니다.
# penguin 데이터에 pairplot을 출력합니다.
sns.pairplot(data=data, hue='species', corner=True)
정사각형 그림에 데이터에 대한 정도 차이를 색 차이로 보여주는 plot.
말 그대로 heatmap이기 때문에, 열화상카메라로 사물을 찍은 것처럼 정보의 차이를 보여줍니다.
pairplot과 비슷하게 feature간 관계를 시각화할 때 많이 사용합니다.
상관관계란?
어떤 X값의 변화에 따라 Y값의 선형적으로 변화하는지를 측정한 지표.
[-1, 1]
e.g. 아이스크림 판매량 증가 <----> 상어에 물린 사람 수
# 각 feature간 상관관계를 파악하기 위해 Correlation matrix를 만듭니다.
data.corr()
# penguin 데이터에 heatmap을 출력합니다.
sns.heatmap(data.corr(), annot=True, fmt='.4f', cmap='viridis')
아무것도 모르는 데이터를 가져와서 EDA를 하려면 어떤 것들을 확인해야할까?
EDA의 process는 어떻게 될까?
EDA를 잘하려면 어떤 것들이 필요할까?
[데이터 링크] https://www.kaggle.com/c/instacart-market-basket-analysis/data
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 데이터를 불러오면 처음 무조건 파악해야하는 것!
aisles = pd.read_csv('./data/aisles.csv')
departments = pd.read_csv('./data/departments.csv')
orders = pd.read_csv('./data/orders.csv')
order_products_train = pd.read_csv('./data/order_products__train.csv')
products = pd.read_csv('./data/products.csv')
orders.info(memory_usage='deep') # 340만개, 350MB
#order_products_train.info() # 140만개
#products.info()
>>> <class 'pandas.core.frame.DataFrame'>
RangeIndex: 3421083 entries, 0 to 3421082
Data columns (total 7 columns):
# Column Dtype
--- ------ -----
0 order_id int64
1 user_id int64
2 eval_set object
3 order_number int64
4 order_dow int64
5 order_hour_of_day int64
6 days_since_prior_order float64
dtypes: float64(1), int64(5), object(1)
memory usage: 358.8 MB
#aisles # 중분류
#departments # 대분류
#products # 실제 품목
#orders
order_products_train
# Q1. 어떤 Product가 가장 많이 reorder가 되었는가?
# Q1-1. product 개수는 49688개인데, Q1의 결과로 왜 39123개만 나올까요?
data = pd.merge(order_products_train, products, on='product_id', how='inner')
pd.pivot_table(data=data, index=['product_name'], values='reordered', aggfunc='sum')
# Q2. 가장 많이 주문된 Product는?
pd.pivot_table(data=data, index=['product_name'], values='reordered', aggfunc='sum')\
.sort_values(by='reordered', ascending=False)
# Q3. reorder가 가장 많이 일어나는 department는?
data = pd.merge(data, departments, on='department_id', how='inner')
data.pivot_table(index='department', values='reordered', aggfunc='sum').sort_values(by='reordered', ascending=False)
data = pd.merge(data, aisles, on='aisle_id')
data
orders = orders[orders.eval_set == 'train']
orders
# Q4. order를 가장 많이 한 user는?
orders.pivot_table(index='user_id', values='order_number', aggfunc='count').sort_values(by='order_number',
ascending=False)
pt = orders.pivot_table(index='user_id', values='order_number', aggfunc='max')
pt.sort_values(by='order_number', ascending=False) # 100
pt[pt.order_number == 100] # order_number는 최소 4에서 최대 100.
# Q5. order가 가장 많이 일어난 요일은?
orders.order_dow.value_counts()
sns.countplot(data=orders, x='order_dow')
# Q6. 재구매율이 높은 product top10은?
data.pivot_table(index='product_name', values='reordered', aggfunc='mean')\
.sort_values(by='reordered', ascending=False)
# Q7. 재구매가 많이 일어나는 요일?
data = pd.merge(data, orders, on='order_id')
data.pivot_table(index='order_dow', values='reordered', aggfunc='sum').sort_values(by='reordered', ascending=False)
# Q8. 사람들이 평균적으로 재구매를 하는 기간은?
data.days_since_prior_order.mean()
>>> 17.0661258672976
# Q9. days_since_prior에 따른 reorder 횟수 변화를 그래프로 그려봅시다.
data.days_since_prior_order.value_counts()
pt = data.pivot_table(index='days_since_prior_order', values='reordered', aggfunc='sum')
pt = pt.reset_index()[:-1]
plt.figure(figsize=(20, 6))
#sns.barplot(x=pt.index.astype(int), y=pt.reordered)
#sns.lineplot(x=pt.index.astype(int), y=pt.reordered, lw=4)
sns.barplot(x=pt.days_since_prior_order.astype(int), y=pt.reordered)
sns.lineplot(x=pt.days_since_prior_order.astype(int), y=pt.reordered, lw=4)
# Q10. user들이 구매 패턴에 따라 어떤 그룹으로 묶일까?
## Customer Segmentation
# Q10-1. user를 어떻게 정의할것인가? (= user profiling) --> 어떤 department에 product를 몇개씩 샀는가 & reordered 비율
pd.pivot_table(data=data[data.user_id == 112108], index='department', values='order_dow', aggfunc='count')
pd.pivot_table(data=data[data.user_id == 72775], index='department', values='order_dow', aggfunc='count')
user_matrix = pd.crosstab(data.user_id, data.department)
user_matrix
# Q10-2. 비슷한 유저를 어떻게 판단할것인가? ---> clustering
from sklearn.cluster import KMeans
model = KMeans(n_clusters=4, random_state=42)
labels = model.fit_predict(user_matrix)
df = pd.DataFrame({'user_id' : user_matrix.index, 'group_id' : labels})
data = pd.merge(data, df, on='user_id')
data
g0 = data[data.group_id == 0]
g0.department.value_counts()[:10]
>>> produce 74240
dairy eggs 62297
beverages 54829
snacks 43126
frozen 36200
pantry 29538
household 19899
bakery 17258
deli 14858
canned goods 14142
Name: department, dtype: int64
g1 = data[data.group_id == 1]
g1.department.value_counts()[:10]
>>> produce 122132
dairy eggs 36285
snacks 15352
frozen 13413
pantry 12897
beverages 11661
canned goods 8983
deli 7423
bakery 7201
dry goods pasta 6835
Name: department, dtype: int64
g2 = data[data.group_id == 2]
g2.department.value_counts()[:10]
>>> produce 162677
dairy eggs 45133
pantry 18989
beverages 18820
frozen 18806
snacks 18451
canned goods 12889
deli 10672
bakery 10666
dry goods pasta 9418
Name: department, dtype: int64
g3 = data[data.group_id == 3]
g3.department.value_counts()[:10]
>>> dairy eggs 73336
produce 50038
snacks 41933
frozen 32007
beverages 28736
pantry 19818
bakery 13269
deli 11338
canned goods 10785
dry goods pasta 10522
Name: department, dtype: int64