📌 학습주제
1. Indian Restaurant Dataset으로 실습하기
2. Global AI, ML, Data Science Salary Dataset 으로 실습하기
3. NBA players Dataset으로 실습하기
Indian Restaurante Dataset으로 실습하기
실습 링크 : https://www.kaggle.com/datasets/abhijitdahatonde/27000-indian-restaurant-dataset
🍜 27,000 + Indian Restaurant Dataset 🏰 🍰
Indian Restaurants dataset
www.kaggle.com
1단계. 데이터 살펴보기
indian_restaurants.csv : 인도내에 있는 식당 정보를 다각도로 제공하는 데이터셋
컬럼명 | 의미 |
restaurant_name | 식당 이름 |
rating | (구글 지도 제공) 평점 |
average_price | 식당 음식의 평균 가격 |
average_delivery_time | 평균 배달 시간 |
south_indian_or_not | 남인도 위치 여부 |
north_indian_or_not | 북인도 위치 여부 |
fast_food_or_not | 패스트푸드 여부 |
street_food | 길거리 음식 여부 |
biryani_or_not | 비르아니 제공 여부 |
bakery_or_not | 제과제빵 여부 |
location | 위치 |
컬럼이 숫자 타입인 경우 히스토그램이 제공되기 때문에 데이터의 분포를 살펴볼 수 있다.
** 히스토그램(histogram)
: 구간별로 데이터가 얼마나 분포하고 있는지를 그래프로 나타낸 것.
** EDA(탐색적 데이터 분석)
: 데이터 셋이 대략적으로 어떻게 생겼는지, 분포는 어떻게 되는지 등 기본 메트릭을 훑어볼 수 있다.
예를들어, 위 데이터셋에서 rating 컬럼의 경우 [3.78 - 3.90] 사이에 가장 많은 값들이 위치하고 있음을 알 수 있다.
반면, [south_indian_or_not], [north_indian_or_not], [fast_food_or_not], [street_food], [biryani_or_not], [bakery_or_not] 컬럼의 경우 인지/아닌지를 판단하는 컬럼이기 때문에 0과 1로 이루어져 있음을 알 수 있다.
예를들어, [south_indian_or_not] 컬럼에서 0은 '남인도에 위치하지 않음'을, 1은 '남인도에 위치함'을 의미한다.
2단계. SQL로 데이터 분석하기
-- 데이터의 중복 확인
SELECT count(1) AS cnt, count(distinct restaurant_name) AS cnt_distinct
FROm restaurant
-- 어떤 데이터가 중복인지 확인
SELECT restaurant_name, count(1) as cnt
FROM restaurant
GROUP BY 1
HAVING cnt > 1
중복 여부를 검사할 때, 컬럼 1개만 빠져도 중복이 발생했다.
즉, 해당 테이블은 행끼리 구분할 수 있는 unique한 key가 없다고 결론지을 수 있다.
-- 식당 이름별로 식당 개수, 평균 평점, 평균 가격, 평균 배달 시간 알아보기
-- 평균 평점이 높은 순으로 상위 20개만 추출
SELECT restaurant_name,
count(1) AS cnt,
avg(rating) AS avg_rating,
avg(average_price) as avg_price,
avg(average_delivery_time) AS avg_delivery_time
FROM restaurant
GROUP BY 1
ORDER BY 3 DESC
LIMIT 20
(1-10:00)
평점이 높은 상위 20개 식당은 모두 cnt값이 1이었다. 즉, 프랜차이즈가 아닌 식당들이 평점이 높은 것을 알 수 있다.
-- 실제로 cnt에 따라서 평균 평점에 차이가 있는지 알아보자
WITH counts AS(
SELECT restaurant_name, count(1) as cnt
FROM restaurant
GORUP BY 1
)
SELECT CASE cnt
WHEN 1 THEN 'cnt-1'
WHEN 2 THEN 'cnt-2'
ELSE 'cnt-ov3'
END AS cnt_group
FROM counts INNER JOIN restaurant ON counts.restaurant_name = restaurant.restaurant_name
GROUP BY 1
점포 개수가 많아질수록 평균 평점이 더 높다는 것을 알 수 있다.
프랜차이즈는 규격화되어있는 항목들이 많기 때문에 서비스나 음식 품질 등이 어느 정도의 수준까지는 보장되어있을 가능성이 높다.
반면, 단일 점포는(cnt = 1) 개성이 뚜렷한 가게들이 많을 것이고, 서비스나 음식 품질 등에 편차가 클 것으로 예상된다.
이러한 생각이 맞는지 히스토그램으로 알아보자.
-- '프랜차이즈일수록 평균 평점에 편차가 적을 것이다.'라는 생각이 맞는지 알아보자.
WITH counts AS(
SELECT restaurant_name, count(1) as cnt
FROM restaurant
GORUP BY 1
)
SELECT counts.restaurant_name, cnt, rating
FROM counts INNER JOIN restaurant ON counts.restaurant_name = restaurant.restaurant_name
GROUP BY 1
(Maria DB 기준) 상단에 File 탭 -> Export -> CSV -> Export
[결론]
1. 점포 개수가 1개인 식당을 '단일 점포', 2개 이상인 식당을 '프렌차이즈'라고 정의
2. 평균 평점 기준으로 상위 20개 식당은 모두 '단일 점포'
3. 평균적으로 '프렌차이즈'의 평점이 '단일 점포'보다 높음
4. (평균 평점 분포 히스토그램을 살펴본 결과) '단일 점포' 일수록 분포가 양 끝으로 더 넓게 퍼져 있다. 양 극단의 값이 더 많다. 즉, '단일 점포'는 점포마다 서비스와 품질 등의 차이가 커서 최상위권과 최하위권의 위치한 점포들은 '단일 점포'일 가능성이 높다.
반면, '프렌차이즈'는 평균적으로 높은 품질의 음식과 서비스를 제공하고 식당 간의 편차가 상대적으로 적다.
[해석]
즉, 인도에 갔을 때 식당을 고르는데 실패하고 싶지 않다면 어느 정도 수준의 맛과 품질이 보장된 '프렌차이즈'를 선택하는 것이 좋다. 반면 실패 리스크를 조금 감수하더라도 나만 아는 최고의 식당을 찾고 싶다면 '단일 점포'를 선택하는 것이 좋을 것이다.
데이터를 살펴보았을 때, 상식과 데이터가 일치하는 경우도 있지만 그렇지 않은 경우도 많다.
따라서 분석이 상식과 일치하지 않는다면 데이터가 잘 들어왔는지, 분석을 제대로 했는지 다시 한번 확인하거나
기존에 갖고 있던 상식을 의심해 볼 필요가 있다.
** 피어슨 상관계수
: -1에서 1 사이의 값을 가지며, 두 변수가 얼마나 상관도가 높은지를 측정하는 지표
-- 식당 이름별로 식당 개수, 평균 평점, 평균 가격, 평균 배달 시간 알아보기
-- 평균 평점이 높은 순으로 전체 데이터 추출
SELECT restaurant_name,
count(1) AS cnt,
avg(rating) AS avg_rating,
avg(average_price) as avg_price,
avg(average_delivery_time) AS avg_delivery_time
FROM restaurant
GROUP BY 1
ORDER BY 3 DESC
(Maria DB 기준) 상단에 File 탭 -> Export -> CSV 탭 -> 'Export' 클릭
(Google Spread sheet) 파일 탭 -> 가져오기 -> 업로드 -> '찾아보기' 클릭 -> 저장한 파일 클릭 후 '열기' -> 가져오기 위치 [새 시트 삽입] -> '데이터 가져오기' 클릭
-- 평균 평점과 점포 개수 간 피어슨 상관계수 구해보기 (google spread sheet)
=CORREL(C2:C21953, B2:B21953)
0.01658311058
양수이므로 점포 개수가 많아질수록 평균 평점이 높아지는 경향을 확인할 수 있다.
-- 평균 평점과 평균 가격 간 피어슨 상관계수 구해보기 (google spread sheet)
=CORREL(C2:C21953, D2:D21953)
-0.00704071026
음수이므로 가격이 비쌀수록 평균 평점이 낮아진다고 생각할 수도 있겠지만, 상관도가 0에 가깝기 때문에 가격과 평점의 상관관계가 매우 약하다고 할 수 있다.
-- 평균 평점과 평균 배달시간 간 피어슨 상관계수 구해보기 (google spread sheet)
=CORREL(C2:C21953, E2:E21953)
-0.0895920226
음수이므로 배달 시간이 길어질수록 평균 평점이 낮아지는 경향을 확인할 수 있다.
→ 그러나 위의 상관계수 모두 점포의 특성이 모두 반영되지 않았기 때문에 상관도가 매우 낮게 나온다.
점포의 특성을 반영해서 상관계수를 다시 구해본다면 위의 결과와 다르게 나올 것이다.
원본데이터를 가지고 원하는 형태로 쪼갠 뒤에 상관계수를 다시 구해보자.
-- 원본 데이터에 점포 개수인 cnt 컬럼이 붙은 형태 출력해서 export
WITH counts AS(
SELECT restaurant_name, count(1) as cnt
FROM restaurant
GORUP BY 1
)
SELECT restaurant.*, cnt
FROM counts INNER JOIN restaurant ON counts.restaurant_name = restaurant.restaurant_name
GROUP BY 1
점포 개수에 따른 평점과 배달 시간의 상관관계
- 점포 개수가 3개 이상인 프렌차이즈에서 음의 상관도가 높게 나옴. 점포가 3개 이상인 경우 배달시간이 길어질수록 평점이 더 낮게 나오는 경향을 보인다.
패스트 푸드점 여부에 따른 평점과 배달 시간의 상관관계
- 패스트 푸드점 여부에 따른 차이는 두드러지지 않는다.
스트릿 푸드점 여부에 따른 평점과 배달 시간의 상관관계
- 스트릿 푸드가 아닌 경우보다 차이가 더 두드러진다
베이커리 여부에 따른 평점과 배달 시간의 상관관계
- 상관도가 상대적으로 떨어진다.
배달 시간과 가격이 상대적으로 큰 도시와 작은 도시에서 상관도가 다른지 알아보기
-- 지역별 식당 수를 구해보자
SELECt location, count(1) as cnt
FROM restaurant
GROUP BY 1
ORDER BY 2 DESC
LIMIT 20
식당이 가장 많은 도시 : Kolkata (cnt = 978)
-- 상위 5%의 도시 이름 추출후 export 하기
WITH cnts as(
SELECT location, count(1) as cnt
FROM restaurant
GROUP BY 1
)
res as(
SELECT location, percent_rank() over (ORDER BY cnt) as cnt_rank
FROM cnts
)
SELECT location
FROM res
WHERE cnt_rank > 0.95
상위 5%의 도시 : Kolkata, NCR, Lucknow, Hyderabad. Chandigarh
-- 식당 수 기준으로 하위 15% 추출하기
WITH cnts as(
SELECT location, count(1) as cnt
FROM restaurant
GROUP BY 1
)
res as(
SELECT location, percent_rank() over (ORDER BY cnt) as cnt_rank
FROM cnts
)
SELECT location
FROM res
WHERE cnt_rank < 0.15
하위 15%의 도시 : Aurangabad, Jammu, Nainital, Darjeeling, Manali, Motihari, Shimla, Gonda, Alappuzha, Firozabad, Junagadh, Dharamshala, Rishikesh, Kharagpur, Darbhanga
(1- 27:04)
대도시와 소도시 그룹을 나눈 뒤에 상관계수 계산
소도시에서 가격과 배달시간 모두에 음의 상관관계가 강하게 나타남
대도시에서는 업체가 조금 더 밀집되어 위치해있기 때문에 배달 시간 차이가 크게 나지 않아서 일수도 있고, 가격이 비슷해서 일수도 있다.
전체 지표를 확인한 후에 다양한 차원에서 그룹을 쪼개서 지표를 확인하고 그 원인을 데이터에서 찾는 방식으로 분석을 이어나갈 수 있다.
점포 종류별로 기본 수치들을 뽑아보자.
-- 지역별 점포 특성 알아보기
SELECT location,
avg(rating) as avg_rating,
avg(average_price) as avg_price,
avg(average_delivery_time) as avg_time,
Max(average_price) as max_price,
count(restaurant_name)
FROM restaurant
GROUP BY 1
ORDER BY 3 DESC
LIMIT 10
- 평균 가격이 가장 비싼 지역 : Shimla
점포 개수가 적은데 비해 max_price가 보통 수준인 것으로 보았을 때 평균 가격이 가장 비싼 지역이 맞다고 할 수 있겠다.
max_price를 살펴본 이유는, 1개 식당이 매우 비싼 식당이어서 평균값을 끌어올린 것인지 아닌지 판단하기 위해서이다.
평균은 이상치(outlier)의 영향을 많이 받기 때문에 평균 기준으로 분석하고 싶을 때는 최댓값/최솟값 까지 확인해보는 것이 좋다.
따라서, 더 좋은 값으로 비교하기 위해서는 평균보다는 중앙값(median)이나 히스토그램을 확인해보는 것이 좋다.
- 평균 가격이 가장 저렴한 지역 : Rishkesh
지역 외에 가격에 영향을 미치는 요소들도 확인해보자.
-- 지역 외 가격에 영향을 미치는 특성 알아보기
-- (1) 남인도 여부
SELECT IF(south_indian_or_not = 0, 'south-0', 'south-1') as group_name,
AVG(average_price) as avg_price
FROM restaurant
GROUP BY 1
UNION
-- (2) 북인도 여부
SELECT IF(north_indian_or_not = 0, 'north-0', 'north-1') as group_name,
AVG(average_price) as avg_price
FROM restaurant
GROUP BY 1
UNION
-- (3) 패스트 푸드점 여부
SELECT IF(fast_food_or_not = 0, 'fast-0', 'fast-1') as group_name,
AVG(average_price) as avg_price
FROM restaurant
GROUP BY 1
UNION
-- (4) 스트릿 푸드점 여부
SELECT IF(street_food = 0, 'street-0', 'street-1') as group_name,
AVG(average_price) as avg_price
FROM restaurant
GROUP BY 1
UNION
-- (5) 비르아니 여부
SELECT IF(biryani_or_not = 0, 'biryani-0', 'biryani-1') as group_name,
AVG(average_price) as avg_price
FROM restaurant
GROUP BY 1
UNION
-- (6) 베이커리 여부
SELECT IF(bakery_or_not = 0, 'bakery-0', 'bakery-1') as group_name,
AVG(average_price) as avg_price
FROM restaurant
GROUP BY 1
- (1) 남인도인 지역이 아닌 지역보다 4루피정도 저렴함
- (2) 북인도인 지역이 아닌 지역보다 4루피정도 비쌈
- (3) 패스트 푸드점인 곳이 아닌 곳보다 3루피정도 저렴함
- (4) 스트릿 푸드점인 곳이 아닌 곳보다 6루피정도 저렴함
- (5) 비르아니가 있는 곳이 없는 곳보다 6루피정도 비쌈
- (6) 베이커리인 곳이 아닌 곳보다 2루피정도 저렴함
↑ 위의 요소들(지역 외 가격에 영향을 미치는 요인들)을 가지고 Shimla보다 Rishkesh가 가격이 저렴한 이유에 대해서 분석해보자.
WITH base as(
SELECT location, south_indian_or_not, north_indian_or_not, fast_food_or_not, street_food, biryani_or_not, bakery_or_not
FROM restaurant
WHERE location in ('Rishikesh', 'Shimla')
)
SELECT location, count(1) as tot_cnt, SUM(south_indian_or_not) as south_cnt,
SUM(north_indian_or_not) as north_cnt, SUM(fast_food_or_not) as fast_cnt,
SUM(street_food) as street_cnt, SUM(biryani_or_not) as biryani_cnt,
SUM(bakery_or_not) as bakery_cnt
FROM base
- street_food 여부 : Rishkesh는 전체 중 36% 식당이 거리 음식을 판매
- south_indian 여부 : Rishkesh는 전체 중 20% 식당이 남인도 스타일의 요리를 판매하는 반면, Shimla에는 남인도 스타일 요리를 판매하지 않음
평균 배달 시간과 평점에 대해서도 분석해보자.
-- 지역 외 가격에 영향을 미치는 특성 알아보기
-- (1) 남인도 여부
SELECT IF(south_indian_or_not = 0, 'south-0', 'south-1') as group_name,
AVG(average_price) as avg_price,
AVG(average_delivery_time) as avg_time, ROUND(AVG(rating), 3) as avg_rating
FROM restaurant
GROUP BY 1
UNION
-- (2) 북인도 여부
SELECT IF(north_indian_or_not = 0, 'north-0', 'north-1') as group_name,
AVG(average_price) as avg_price
AVG(average_delivery_time) as avg_time, ROUND(AVG(rating), 3) as avg_rating
FROM restaurant
GROUP BY 1
UNION
-- (3) 패스트 푸드점 여부
SELECT IF(fast_food_or_not = 0, 'fast-0', 'fast-1') as group_name,
AVG(average_price) as avg_price
AVG(average_delivery_time) as avg_time, ROUND(AVG(rating), 3) as avg_rating
FROM restaurant
GROUP BY 1
UNION
-- (4) 스트릿 푸드점 여부
SELECT IF(street_food = 0, 'street-0', 'street-1') as group_name,
AVG(average_price) as avg_price
AVG(average_delivery_time) as avg_time, ROUND(AVG(rating), 3) as avg_rating
FROM restaurant
GROUP BY 1
UNION
-- (5) 비르야니 여부
SELECT IF(biryani_or_not = 0, 'biryani-0', 'biryani-1') as group_name,
AVG(average_price) as avg_price
AVG(average_delivery_time) as avg_time, ROUND(AVG(rating), 3) as avg_rating
FROM restaurant
GROUP BY 1
UNION
-- (6) 베이커리 여부
SELECT IF(bakery_or_not = 0, 'bakery-0', 'bakery-1') as group_name,
AVG(average_price) as avg_price
AVG(average_delivery_time) as avg_time, ROUND(AVG(rating), 3) as avg_rating
FROM restaurant
GROUP BY 1
- (1) 남인도인 지역이 아닌 지역보다 배달시간은 빠르고 평점은 높음
- (2) 북인도인 지역이 아닌 지역보다 배달시간은 느리고 평점은 낮음
- (3) 패스트 푸드점인 곳이 아닌 곳보다 배달시간은 느리고 평점은 낮음
- (4) 스트릿 푸드점인 곳이 아닌 곳보다 배달시간은 빠르고 평점은 낮음
- (5) 비르야니가 있는 곳이 없는 곳보다 배달시간은 느리고 평점은 낮음
- (6) 베이커리인 곳이 아닌 곳보다 배달시간은 빠르고 평점은 낮음
→ 평균 평점의 차이가 큰 요인은 베이커리 유무와 비르야니 유무이다.
지역별 평균 평점이 가장 높은 지역과 낮은 지역을 비교해보자.
SELECT locaation, avg(rating) as avg_rating, count(1) as cnt
FROM restaurant
GROUP BY 1
ORDER BY 2 DESC
LIMIT 10
- 평균 평점이 가장 높은 곳 : Junagadh
- 평균 평점이 가장 낮은 곳 : Aurangabad이지만 식당이 1개 밖에 없으므로 제외. 따라서 Motihari가 된다.
↑ 위의 요소들(지역 외 가격에 영향을 미치는 요인들)을 가지고 Junagadh 보다 Motihari가 가격이 저렴한 이유에 대해서 분석해보자.
WITH base as(
SELECT location, south_indian_or_not, north_indian_or_not, fast_food_or_not, street_food, biryani_or_not, bakery_or_not
FROM restaurant
WHERE location in ('Motihari', 'Junagadh')
)
SELECT location, count(1) as tot_cnt, SUM(south_indian_or_not) as south_cnt,
SUM(north_indian_or_not) as north_cnt, SUM(fast_food_or_not) as fast_cnt,
SUM(street_food) as street_cnt, SUM(biryani_or_not) as biryani_cnt,
SUM(bakery_or_not) as bakery_cnt
FROM base
- bakery 여부 : Motihari는 전체 중 12% 식당이 베이커리인 반면, Junagadh는 5% 식당이 베이커리이다.
- biryani 여부 : Motihari는 전체 중 44% 식당이 비르야니를 판매하는 반면, Junagadh는 8% 식당이 비르야니를 판매한다.
→ bakery가 아니고 biryani가 아니면 평균 평점이 높은 경향이 있음을 알 수 있다.
3단계. 분석 결론 내리기
특성 그룹별 평균을 구해서 가격이 가장 비싼 지역과 저렴한 지역, 평점이 가장 높은 지역과 낮은 지역을 비교했다.