in Yelp Dataset Challenge Round 10
지난 포스팅에서 Yelp 데이터(Yelp dataset challenge round 10)를 뜯어봤는데, 이번에는 조금 더 나아가 Yelp에서 제공하는 장소 카테고리는 어떤 것이 있고 몇 개나 있는지 알아보려 한다.
이용할 데이터는 지난번에 json to csv로 변환한 'business.csv' 파일이다. 실험에 사용한 언어는 Python 3.5.2이고 IDE로 jupyter notebook을 활용했다. 그리고 이번 포스팅에서 사용한 코드와 결괏값은 맨 아래에 첨부파일로 넣었다.
목차
1. 파일 불러와서 데이터 속성 확인하기
2. 필요한 속성('categories')만 추출하기
3. 중복 제거하기
4. 카테고리별 개수 카운트하기
# business.csv 파일을 list 형태로 불러오기
biz = []
g = open('business.csv', 'r', encoding='utf-8')
rdrg = csv.reader(g)
for line in rdrg:
biz.append(line)
# business.csv 파일의 속성 확인하기
biz[0]
['attributes.Ambience.divey', 'attributes.RestaurantsDelivery', 'attributes.DogsAllowed', 'postal_code', 'hours.Thursday', 'attributes.HairSpecializesIn.coloring', 'attributes.BestNights.sunday', 'attributes.BYOB', 'attributes.AgesAllowed', 'attributes.Music.video', 'hours.Friday', 'latitude', 'attributes.Alcohol', 'attributes.Ambience.classy', 'attributes.RestaurantsTableService', 'business_id', 'attributes.Ambience.touristy', 'attributes.RestaurantsCounterService', 'attributes.Corkage', 'attributes.RestaurantsGoodForGroups', 'categories', 'name', 'attributes.BusinessAcceptsBitcoin', 'attributes.HappyHour', 'attributes.WheelchairAccessible', 'attributes.Ambience.hipster', 'attributes.BusinessAcceptsCreditCards', 'is_open', 'attributes.DietaryRestrictions.vegetarian', 'attributes.Music.live', 'attributes.Music.background_music', 'neighborhood', 'attributes.BusinessParking.lot', 'attributes.Music.karaoke', 'review_count', 'attributes.GoodForMeal.breakfast', 'attributes.NoiseLevel', 'attributes.HairSpecializesIn.perms', 'state', 'attributes.DriveThru', 'attributes.HasTV', 'attributes.GoodForMeal.dinner', 'attributes.BusinessParking.street', 'address', 'attributes.RestaurantsAttire', 'hours.Sunday', 'attributes.BestNights.tuesday', 'attributes.AcceptsInsurance', 'attributes.BestNights.wednesday', 'hours.Wednesday', 'attributes.HairSpecializesIn.kids', 'attributes.Open24Hours', 'attributes.Ambience.trendy', 'attributes.CoatCheck', 'hours.Monday', 'attributes.HairSpecializesIn.straightperms', 'city', 'attributes.HairSpecializesIn.curly', 'attributes.Music.no_music', 'hours.Tuesday', 'attributes.HairSpecializesIn.africanamerican', 'stars', 'attributes.RestaurantsPriceRange2', 'attributes.Ambience.intimate', 'attributes.GoodForMeal.latenight', 'attributes.GoodForMeal.dessert', 'attributes.BusinessParking.validated', 'attributes.GoodForMeal.lunch', 'attributes.GoodForKids', 'attributes.DietaryRestrictions.soy-free', 'attributes.GoodForMeal.brunch', 'attributes.BusinessParking.valet', 'longitude', 'attributes.DietaryRestrictions.gluten-free', 'attributes.BYOBCorkage', 'attributes.BusinessParking.garage', 'attributes.BestNights.friday', 'hours.Saturday', 'attributes.Music.dj', 'attributes.HairSpecializesIn.extensions', 'attributes.BestNights.saturday', 'attributes.Ambience.casual', 'attributes.BestNights.thursday', 'attributes.BestNights.monday', 'attributes.HairSpecializesIn.asian', 'attributes.DietaryRestrictions.kosher', 'attributes.WiFi', 'attributes.Smoking', 'attributes.DietaryRestrictions.halal', 'attributes.GoodForDancing', 'attributes.ByAppointmentOnly', 'attributes.Caters', 'attributes.RestaurantsReservations', 'attributes.DietaryRestrictions.dairy-free', 'attributes.DietaryRestrictions.vegan', 'attributes.Ambience.romantic', 'attributes.Music.jukebox', 'attributes.Ambience.upscale', 'attributes.RestaurantsTakeOut', 'attributes.BikeParking', 'attributes.OutdoorSeating']
위에 출력된 속성 101개 중 몇 개를 보면, 먼저 'business_id'는 해당 POI의 unique id이며, 'name'은 해당 POI의 상호명을 나타낸다. 'hours.Thursday'처럼 'hours.'와 요일이 결합된 속성은 예상하다시피 월요일(Monday)부터 일요일(Sunday)까지 총 7개가 있다. 이는 요일별 오픈 시간을 명시한 것으로, 사용자 입장에서 아주 유용한 정보다.
'attributes.RestaurantsDelivery'는 배달이 가능한지 여부를 나타내며 True or False를 출력하는데, 대부분의 값이 비어(NaN) 있다. 'attributes.DogsAllowed'는 애완견을 동반할 수 있는지 여부를 나타내며 역시 True or False를 출력하는데, 대부분의 값이 비어 있다.
101개 속성 중 "위치 정보"를 나타내는 속성은 어떤 것들이 있을까?
: 출력된 순서대로 보면, 'postal_code', 'latitude', 'state', 'address', 'city', 'longitude' 이렇게 6개가 있다. 이렇게 친절하게 경위도 좌표가 찍혀 있는 데이터는 정말 소중하다. 소중히 다뤄야 한다.
이해를 돕기 위해 101개 속성과 속성별 값 일부를 표로 정리해 보았다(표 1-1, 표 1-2). 참고로 번호(No)는 0부터 시작한다.
101개 속성 중 지금 우리에게 필요한 것은 20번째 속성인 'categories'이다. 표 1-1에서 'categories' 행을 확인해 보면, 하나의 장소에 여러 개의 카테고리가 포함된 것을 알 수 있다. 이것들을 가져와 보자.
# 'categories'만 추출
cate = []
for i in range(1, len(biz)): # 제목 줄은 제외하므로 1부터 시작
cate.append(biz[i][20])
# 데이터 type 확인
type(cate[0]) # str
list인 'cate' 내부값의 type이 string이므로 이를 list로 바꿔줘야 한다.
# str to list
cate2 = []
for i in range(0, len(cate)):
cate2.append([ast.literal_eval(cate[i])])
# 데이터 확인
cate2[0] # [['Shopping', 'Shopping Centers']]
그리고 'cate2'는 list 안의 list 형태로 돼 있는 것을 알 수 있는데, 이것을 해체해서 하나의 list로 만든 다음에 중복 제거를 해 보자.
# list 해체하기
cate3 = []
for i in range(0, len(cate2)):
for j in range(0, len(cate2[i][0])):
cate3.append(cate2[i][0][j])
# 데이터 길이 확인
len(cate3) # 590,290
중복 제거 전의 카테고리 개수는 590,290개이다. 별 의미 없는 숫자다. 이제 이것의 중복 제거를 해 보자.
# 중복 제거하기
cate4 = list(set(cate3))
# 데이터 길이 확인
len(cate4) # 1,240
중복 제거 결과 카테고리의 개수가 1,240개로 줄어들었다. 앞에 10개 데이터를 출력해 보자.
cate4[0:10]
['Property Management',
'Body Shops',
'Dance Clubs',
'Personal Care Services',
'Motorsport Vehicle Dealers',
'Home Cleaning', 'Shutters',
'Roof Inspectors',
"Children's Museums",
'Insulation Installation']
위 출력 결과를 보면 순서가 제멋대로인 것을 알 수 있다. 이것을 알파벳순으로 정리하고 다시 출력해 보자.
cate4.sort()
cate4[0:10] # 10개만 출력
['& Probates',
'3D Printing',
'ATV Rentals/Tours',
'Acai Bowls', 'Accessories',
'Accountants',
'Acne Treatment',
'Active Life',
'Acupuncture',
'Addiction Medicine']
앞에서 장소 카테고리 개수가 1,240개라는 것을 확인했는데, 이번에는 각 카테고리가 몇 개씩 있는지 알아보자. 먼저 단어 개수를 카운트하는 함수를 만들었다.
# 단어 개수 카운트하는 함수 만들기
def count_words(word_set, word):
count = 0
for w in word_set:
if word == w:
count += 1
return count
그리고 앞에서 중복 제거 전의 'cate3'과 알파벳순으로 정렬한 'cate4'를 이용해서 각 카테고리에 해당하는 장소의 개수를 카운트해 보자.
# 각 카테고리에 해당하는 장소 개수 카운트하기
cate_count = []
for i in range(0, len(cate4)):
cate4_i = cate4[i]
cate_count.append([cate4_i, count_words(cate3, cate4_i)])
# 값 확인
cate_count[0:10] # 10개만 출력
[['& Probates', 37],
['3D Printing', 5],
['ATV Rentals/Tours', 40],
['Acai Bowls', 37],
['Accessories', 1600],
['Accountants', 214],
['Acne Treatment', 11],
['Active Life', 7427],
['Acupuncture', 503],
['Addiction Medicine', 7]]
자, 여기서 가장 많은 카테고리의 개수를 알고 싶다면? 카테고리 개수를 기준으로 정리해서 상위 10개 값을 뽑아보자.
# 정렬하려면 먼저 tuple로 만들어 줘야 함
# list to tuple
cate_count_t = []
for i in range(0, len(cate_count)):
cate_count_t.append(tuple(cate_count[i]))
# 그리고 정렬
cate_count_sorted = sorted(cate_count_t, key=lambda cate_count_t: cate_count_t[1], reverse=True)
# 상위 10개 값 확인
cate_count_sorted[0:10]
[('Restaurants', 51613),
('Shopping', 24595),
('Food', 23014),
('Beauty & Spas', 15139),
('Home Services', 13202),
('Health & Medical', 12033),
('Nightlife', 11364),
('Bars', 9868),
('Automotive', 9476),
('Local Services', 9343)]
위 결과를 보면, 레스토랑('Restaurants')의 개수가 51,613개로 압도적으로 많고 다음으로 쇼핑('Shopping')과 음식('Food') 관련 장소가 많은 것을 알 수 있다. 레스토랑과 음식은 카테고리 내에서 상당히 클러스터링 되어 있을 것 같다. 다음번에 카테고리별 클러스터링도 한번 해 봐야겠다.
이번 포스팅에서 사용한 코드(.ipynb)와 결괏값인 'cate_count_sorted' 파일(.csv)을 첨부파일로 넣었습니다. 필요하시면 가져다 쓰셔도 됩니다.