본문 바로가기
추천시스템 앱 개발/데이터

[데이터 분석] Movielens(무비렌즈) 데이터 분석 1 - 평점 분포 분석

by 직_장인 2025. 3. 9.

1. 데이터 분석 1

  • 데이터 분석 1(첫번째)로, 데이터 안에 어떤 특징들이 있는지 파악하기 위해 기본적인 데이터 분석을 해보고자 한다.
  • Movielens 데이터는 영화 별점 데이터로, 당연하게도 "영화, 유저(유저 활동), 점수"가 중요한 데이터이다.
  • 각각에 대해 자세히 살펴보자.

1-0. 데이터 분석 준비

 

[데이터 분석] 환경설정 - 패키지 설치, 가상환경 설정

1. 패키지란?python으로 데이터를 살펴보기 위해 패키지를 설치한다.패키지는 복잡한 기능을 손쉽게 사용 가능하도록 사람들이 미리 만들어놓은 도구같은 것이다.간단한 설치만으로 좋은 도구를

work-master.tistory.com

  • 패키지 준비
    • 앞서 가상환경을 활성화하고, 패키지 설치 방법을 알아보았다.
    • 데이터 분석에는 분석을 위한 패키지, 시각화를 위한 패키지 등 여러 종류의 패키지를 사용하게 된다.
    • 아래 코드를 이용하여 jupyter notebook에서 사용할 패키지를 불러온다.
    • 아직 설치하지 않은 패키지를 불러오려는 경우 "No module named '(패키지 이름)'" 이런 오류가 발생할 것이다.
    • 필요한 패키지는 때에 따라 설치하면 된다.
    • 시각화 시 한글도 사용할 것이기 때문에 한글폰트도 설정한다.(Mac 기준)
import pandas as pd
import numpy as np
import seaborn as sns
from datetime import datetime
import networkx as nx
from collections import Counter
from textblob import TextBlob

import matplotlib.pyplot as plt
from matplotlib import rc

# Mac에서 한글 폰트 설정
rc('font', family='AppleGothic')
plt.rcParams['axes.unicode_minus'] = False  # 마이너스 기호 깨짐 방지
  • 데이터 불러오기
    • pandas의 read_csv() 함수를 이용하여 각 파일을 불러온다.
    • len() 함수를 이용하여 각 데이터의 수를 출력해본다.
    • 유저 수는 unique() 함수를 이용하여, rating 데이터에서 userId 중 중복되는 값을 빼고 유일한(unique) 값만 가져와서 수를 확인한다.
# 데이터 로드
movies = pd.read_csv('ml-latest-small/movies.csv')
ratings = pd.read_csv('ml-latest-small/ratings.csv')
tags = pd.read_csv('ml-latest-small/tags.csv')
links = pd.read_csv('ml-latest-small/links.csv')

print(f"영화 수: {len(movies)}")
print(f"평점 수: {len(ratings)}")
print(f"태그 수: {len(tags)}")
print(f"유저 수: {len(ratings['userId'].unique())}")
# 결과
영화 수: 9742
평점 수: 100836
태그 수: 3683
유저 수: 610

 

  • 데이터 살펴보기
    • 각 데이터가 어떤 column으로 이루어져 있는지 확인하고, 샘플로 3개씩 출력한다.
    • 영화(Movies) 데이터에는 '장르'가 있고, 여러 장르인 경우 '|' 로 구분할 수 있다.
    • 점수(Ratings) 데이터에는 점수와 언제 점수를 주었는지 시간이 표시되어 있다.
    • 유저 활동(Tags) 데이터에는 유저가 영화에 남긴 Tag 정보가 표시되어 있다.
    • Links 데이터는 영화 id에 대한 정보가 있으며 분석에는 사용하지 않을 예정이다.
    • display() 함수는 pandas의 dataframe 형태를 보기좋게 출력하는 함수인데, print() 함수로 출력해도 상관은 없다.
# 각 데이터프레임의 컬럼 확인
print("Movies 데이터프레임 컬럼:", movies.columns.tolist())
print("Ratings 데이터프레임 컬럼:", ratings.columns.tolist())
print("Tags 데이터프레임 컬럼:", tags.columns.tolist())
print("Links 데이터프레임 컬럼:", links.columns.tolist())

# 데이터프레임 샘플 확인
print("\nMovies 샘플:")
display(movies.head(3))
print("\nRatings 샘플:")
display(ratings.head(3))
print("\nTags 샘플:")
display(tags.head(3))
# 결과
Movies 데이터프레임 컬럼: ['movieId', 'title', 'genres']
Ratings 데이터프레임 컬럼: ['userId', 'movieId', 'rating', 'timestamp']
Tags 데이터프레임 컬럼: ['userId', 'movieId', 'tag', 'timestamp']
Links 데이터프레임 컬럼: ['movieId', 'imdbId', 'tmdbId']

Movies 샘플:
movieId	title	genres
0	1	Toy Story (1995)	Adventure|Animation|Children|Comedy|Fantasy
1	2	Jumanji (1995)	Adventure|Children|Fantasy
2	3	Grumpier Old Men (1995)	Comedy|Romance

Ratings 샘플:
userId	movieId	rating	timestamp
0	1	1	4.0	964982703
1	1	3	4.0	964981247
2	1	6	4.0	964982224

Tags 샘플:
userId	movieId	tag	timestamp
0	2	60756	funny	1445714994
1	2	60756	Highly quotable	1445714996
2	2	60756	will ferrell	1445714992

 

1-1. 평점 분포 분석

  • MovieLens에서 평점은 0.5점에서 5.0점까지 0.5점 단위로 되어있다.
  • 영화 평점의 분포를 이해하는 것은 유저 행동을 파악하는 첫 단계이다.

1-1-1. 영화 평점 분포

  • 평점 통계량 계산
    • describe() 함수를 이용하여 ratings 데이터의 ['rating'] 컬럼의 상세 정보(describe)를 확인한다.
# 평점 기술 통계량
rating_stats = ratings['rating'].describe()
print("평점 통계량:")
print(rating_stats)
# 결과
평점 통계량:
count    100836.000000
mean          3.501557
std           1.042529
min           0.500000
25%           3.000000
50%           3.500000
75%           4.000000
max           5.000000
Name: rating, dtype: float64
  • 평점 비율 계산
    • ratings 데이터의 ['rating'] 컬럼을 value_counts() 함수로 '점수 별 갯수'를 구한 후 sort_index() 함수로 정렬한다.
    • '점수 별 갯수'를 '전체 점수 갯수'로 나누어 "점수 별 비율"을 계산한다.
    • 그리고 점수 별로 비율값을 for 문을 이용하여 차례대로 출력한다.
# 평점 비율 계산
rating_counts = ratings['rating'].value_counts().sort_index()
rating_percentages = rating_counts / len(ratings) * 100
print("\n평점별 비율(%):")
for rate, pct in zip(rating_percentages.index, rating_percentages.values):
    print(f"{rate}점: {pct:.2f}%")
# 결과
평점별 비율(%):
0.5점: 1.36%
1.0점: 2.79%
1.5점: 1.78%
2.0점: 7.49%
2.5점: 5.50%
3.0점: 19.88%
3.5점: 13.03%
4.0점: 26.60%
4.5점: 8.48%
5.0점: 13.10%
  • 시각화
    • 시각화는 seaborn 이라는 패키지를 이용한다.
    • histplot 을 이용하여 평점의 histogram 그래프를 출력한다.
# 평점 분포 시각화
plt.figure(figsize=(10, 6))
sns.histplot(ratings['rating'], bins=10, kde=True)
plt.title('영화 평점 분포', fontsize=15)
plt.xlabel('평점', fontsize=12)
plt.ylabel('빈도', fontsize=12)
plt.xticks(np.arange(0.5, 5.5, 0.5))
plt.grid(True, alpha=0.3)
plt.show()

  • 분석 결과
    • 평균 평점은 약 3.5점으로, 유저들은 일반적으로 긍정적인 평가를 많이 남기는 경향이 있다.
    • 4점 평점이 가장 많은 비중을 차지하며, 이는 유저들이 자신이 좋아하는 영화에 대해 평가하는 경향이 있음을 시사한다.
    • 2점 이하의 낮은 평점은 상대적으로 적은 비중을 차지한다.

 

1-1-2 유저 별 평균 평점 분포

  • 유저 별 평균 평점 계산
    • groupby() 함수는 그룹을 묶어주는 함수이다.
    • 유저 ID 별로 묶고, 각 유저가 평균(mean)적으로 점수(rating)를 어떻게 줬는지 확인한다.
    • nlargest(), nsmallest() 함수는 pandas에서 제공하는 함수로, pandas의 dataframe에서 값이 큰 순서대로, 작은 순서대로 정렬해주는 함수이다. 각 10개씩 확인해본다.
# 유저 별 평균 평점 분포
user_avg_ratings = ratings.groupby('userId')['rating'].mean()

# 관대한 평가자와 엄격한 평가자 식별
generous_raters = user_avg_ratings.nlargest(10)
strict_raters = user_avg_ratings.nsmallest(10)

print("가장 관대한 평가자 (높은 평균 평점):")
print(generous_raters)
print("\n가장 엄격한 평가자 (낮은 평균 평점):")
print(strict_raters)
# 결과
가장 관대한 평가자 (높은 평균 평점):
userId
53     5.000000
251    4.869565
515    4.846154
25     4.807692
30     4.735294
523    4.693333
348    4.672727
171    4.634146
452    4.556931
43     4.552632
Name: rating, dtype: float64

가장 엄격한 평가자 (낮은 평균 평점):
userId
442    1.275000
139    2.144330
508    2.145833
153    2.217877
567    2.245455
311    2.339286
298    2.363685
517    2.386250
308    2.426087
3      2.435897
Name: rating, dtype: float64
  • 유저 별 평균 평점/갯수 계산
    • 평균 평점만 확인하는건 뭔가 찜찜하다.
    • 만약 5점 준 사람이 영화 하나에 대해서 5점을 줬는데,  평균적으로 5점을 준다고 말하긴 어렵다.
    • 평점 계산에 사용된 데이터 수(count)도 같이 확인해보자.
    • groubpy() 함수의 agg(aggregation)를 이용하여 평균과 갯수를 같이 확인할 수 있다.
    • 그리고 rename을 이용하여 column 명을 바꿔준다.
# 유저 별 평균 평점과 평가 개수 계산
user_stats = ratings.groupby('userId')['rating']\
    .agg(['mean', 'count'])\
    .rename(columns={'mean': 'avg_rating', 'count': 'rating_count'})

# 관대한 평가자와 엄격한 평가자 식별 (평균 평점 기준)
generous_raters = user_stats.nlargest(10, 'avg_rating')
strict_raters = user_stats.nsmallest(10, 'avg_rating')

print("가장 관대한 평가자 (높은 평균 평점):")
print(generous_raters)

print("\n가장 엄격한 평가자 (낮은 평균 평점):")
print(strict_raters)
# 결과
가장 관대한 평가자 (높은 평균 평점):
        avg_rating  rating_count
userId                          
53        5.000000            20
251       4.869565            23
515       4.846154            26
25        4.807692            26
30        4.735294            34
523       4.693333            75
348       4.672727            55
171       4.634146            82
452       4.556931           202
43        4.552632           114

가장 엄격한 평가자 (낮은 평균 평점):
        avg_rating  rating_count
userId                          
442       1.275000            20
139       2.144330           194
508       2.145833            24
153       2.217877           179
567       2.245455           385
311       2.339286            28
298       2.363685           939
517       2.386250           400
308       2.426087           115
3         2.435897            39
  • 통계 결과
    • 평점을 높게주는 사람의 경우 평점 입력 횟수가 그다지 많지 않은걸 볼 수 있다.
    • 통계적으로 30개 미만은 제거해서 볼 수도 있지만, 이정도면 그냥 사용해도 될 것 같다.
  • 시각화
plt.figure(figsize=(10, 6))
sns.histplot(user_avg_ratings, bins=30, kde=True)
plt.title('유저 별 평균 평점 분포', fontsize=15)
plt.xlabel('평균 평점', fontsize=12)
plt.ylabel('사용자 수', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()

  • 분석 결과
    • 일부 유저는 대부분의 영화에 매우 높은 평점을 주는 반면, 다른 유저들은 더 비판적인 시각으로 평가한다.
    • 이러한 유저 편향은 추천 시스템을 설계할 때 고려해야 할 중요한 요소이다.

댓글