곡 별 메타 데이터 : song_meta.json
song_meta = pd.read_json('song_meta.json', typ = 'frame')
pandas를 이용하여 json파일을 읽어들일 수 있다. typ을 frame으로 지정 해 준다. 문서에는 다음과 같이 나와있다. "Convert a JSON string to pandas object."
type ='frame'을 제외하면 어떻게 읽어들이는지 궁금하니 한번 시도 해 보았다.제외 하든 안하는 둘다. data frame임을 확인 할 수 있었다.문서를 보니 typ에는 {‘frame’, ‘series’}두가지가 들어갈 수 있고 default는 ‘frame’이다.
곡 장르 코드 데이터 :genre_gn_all.json을 읽었을때는 series로 읽었다. 근데 Frame으로 읽으면 어떻게 되는가? 안읽힌다. 왜 그러는가?자세하게 공부하고 싶다면 :pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_json.html 를 참고하여 예제를 풀어보자(이번시간에는 넘어가도록 하겠다.)
곡별 매핑 장르 수를 알아보기 위해서 코드를 짜보자.
# 곡 아이디(id)와 대분류 장르코드 리스트(song_gn_gnr_basket) 추출
song_gnr_map = song_meta.loc[:, ['id', 'song_gn_gnr_basket']]
왜 굳이 loc를 썼을까? song_meta[['id', 'song_gn_gnr_basket']] <--이렇게 하면 되는데...
loc를 이용해서 가져온 객체에 값을 할당하면 기존 df를 변경가능하다.
# unnest song_gn_gnr_basket
song_gnr_map_unnest = np.dstack(
(
np.repeat(song_gnr_map.id.values, list(map(len, song_gnr_map.song_gn_gnr_basket))),
np.concatenate(song_gnr_map.song_gn_gnr_basket.values)
)
)
map(func, *iterables) --> map object
Make an iterator that computes the function using arguments from each of the iterables. Stops when the shortest iterable is exhausted.
map함수는 func을 이용하여 *iterables(여기서는 모든곡에대한 '장르 코드의 basket' 들의 집합). 의 iterator를 만들어낸다.
이 결과 인덱스는 song_id, 원소는 'basket에 들어있는원소개수' 인 리스트가 만들어진다.
이를 이용해서 np.repeat의 함수의 인자로 호출하면. song_gnr_map.id.values의 해당 index값에 따라 반복하여 id값을 담은 배열을 만들어 낸다.
np.concatenate를 이용하면
이것을
이렇게 변경할 수 있다.
캡처 3과 캡처 5를 이용해서 np.dstack이라는 함수를 또 호출하는데.
numpy.dstack([배열1, 배열2])를 이용하여 새로운 축으로 배열1과 배열2를 이어 붙일 수 있습니다.
numpy.stack([a, b], axis=2)과 동일한 결과를 반환합니다.
# unnested 데이터프레임 생성 : song_gnr_map
song_gnr_map = pd.DataFrame(data = song_gnr_map_unnest[0], columns = song_gnr_map.columns)
song_gnr_map['id'] = song_gnr_map['id'].astype(str)
song_gnr_map.rename(columns = {'id' : 'song_id', 'song_gn_gnr_basket' : 'gnr_code'}, inplace = True)
# unnest 객체 제거
del song_gnr_map_unnest
np.stack을 이용해서 만든 객체를 위 코드를 이용하여 데이터프레임으로 변환한다.
song_gnr_count = song_gnr_map.groupby('song_id').gnr_code.nunique().reset_index(name = 'mapping_gnr_cnt')
groupby함수는? --> 이에 대해서 실습을 한번 하고 오자 (rfriend.tistory.com/383)
A groupby operation involves some combination of splitting the object, applying a function, and combining the results. This can be used to group large amounts of data and compute operations on these groups.
데이터를 '그룹별로 나누고', '함수를 적용' 하고 , '결과를 합치는' 단계를 거친다.
groupby 함수는 그룹별로 데이터를 집계, 요약하는 방법
groupt에 대한 결과는 다음과 같은데 ...
nunique() 함수는?
Count distinct observations over requested axis.
Return Series with number of distinct observations. Can ignore NaN values.
구분된 관측치를 count한다.
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.nunique.html
reset_index(name = 'mapping_gnr_cnt')
이렇게 해서 대부분의 곡들이 한개의 대분류 장르와 매핑되어 있으며 전체 곡의 약 13퍼센트는 2개 이상의 대분류 장르를 가짐을 알 수 있다. (세부 장르가 아니라. 대분류 장르!
가장 많이 매핑 되는 장르는?
1. 장르 별 곡 수 count 테이블 생성 : gnr_count
아까 구해놓았던 song_id, gnr_code가 있는 데이터 프레임을 가지고
gnr_code별로 grouping한 후 그룹별 원소의 개수를 센 후 reset_index를 한다.
중간 과정의 결과를 차례대로 보면.
위와 같다.
2. 1번 테이블과 장르 meta와 join
gnr_count = pd.merge(gnr_count, gnr_code.loc[:, ['gnr_code', 'gnr_name']], how = 'left', on = 'gnr_code')
gnr_count['gnr_code_name'] = gnr_count['gnr_code'] + ' (' + gnr_count['gnr_name'] + ')'
이제
이 테이블과 gnr_count를 left merge한다. 이는 gnr_count를 기준으로 'gnr_code'열을 이용하여 merge하겠다는것.
결과는 위와 같다.
매핑이 되지 않는 곡들을 제거하고 이를 내림차순으로 정리하면 다음과 같다.
# 3. 매핑이 되지 않은 일부 곡들은 제거
gnr_count = gnr_count[['gnr_code_name', 'song_cnt']].dropna()
# 4. 많은 곡이 매핑된 순 기준으로 내림차순 리스트 생성
gnr_list_desc = gnr_count.sort_values('song_cnt', ascending = False).gnr_code_name
# 5. plotting
gnr_code_name_plot = sns.barplot(x = 'gnr_code_name', y = 'song_cnt', data = gnr_count, order = gnr_list_desc)
gnr_code_name_plot.set_title('장르 별 매핑된 곡 수 분포')
gnr_code_name_plot.set_xlabel('대분류 장르코드')
gnr_code_name_plot.set_ylabel('곡 수')
plt.xticks(rotation = 90)
plt.show()
이를 ploting한다.
발매 년도 별 곡 비중은?
# 1. 곡 아이디(id)와 발매일자(issue_date) 추출
song_issue_date = song_meta[['id', 'issue_date']]
song_issue_date['issue_date'] = song_issue_date['issue_date'].astype(str)
# 2. issue_date의 앞자리 네 자리를 추출하여 발매년도(issue_year) 변수 생성
song_issue_date['issue_year'] = song_issue_date['issue_date'].str[0:4]
song_issue_date.rename(columns = {'id' : 'song_id'}, inplace = True)
song_issue_date['song_id'] = song_issue_date['song_id'].astype(str)
# 3. 1990년도~ 필터링
song_issue_date_filter = song_issue_date[song_issue_date.issue_year >= '1990']
# 4. 발매년도 별 곡 수 count 테이블 생성 : issue_year_song_cnt
issue_year_song_cnt = song_issue_date_filter.groupby('issue_year').song_id.nunique().reset_index(name = 'song_cnt')
# 5. plotting
issue_year_song_cnt_plot = sns.barplot(x = 'issue_year', y = 'song_cnt', data = issue_year_song_cnt, color = 'grey')
issue_year_song_cnt_plot.set_title('발매년도 별 곡 수 추이 (1990년~)')
issue_year_song_cnt_plot.set_xlabel('발매년도')
issue_year_song_cnt_plot.set_ylabel('곡 수')
plt.xticks(rotation = 50)
plt.show()
자꾸 커널이 죽는데 이유가 무엇일까?....
# 2. 1번 테이블 plylst_song_tag_map + 곡 장르 테이블 song_gnr_map join
plylst_song_tag_map = pd.merge(plylst_song_tag_map, song_gnr_map, how = 'left', left_on = 'songs', right_on = 'song_id')
이 두 친구를 songs를 기준으로 left join하는 것인데.! 계속 커널이 죽는다.