Programming/CodingTest

[Data]Web log에서 유저별 페이지 방문 통계

wave35 2024. 10. 3. 20:17

문제

웹 로그 데이터를 분석하여 각 페이지마다 유저들이 방문했는지 통계를 도출하고,

유저의 나이와 성별을 기반으로 상세 통계도 구하시오.

 

주어진 웹 로그 데이터와 두 개의 테이블을 이용하여

방문 페이지별로 유저 방문 수를 계산하고,

유저의 나이대를 그룹화하여 각 페이지에서의 성별 통계를 포함한 결과를 출력해야 합니다.

 

 

요구사항

- 각 페이지별로 유저 방문 수를 계산하시오.

- 유저 정보를 기반으로 나이와 성별에 따라 그룹화하여, 유저 방문 수 통계를 구하시오.

- 10대에서 20살인 경우는 10~20으로, 30대는 30~40으로, 50세 이상은 50~으로 표시합니다.

- 유저 정보가 없는 경우, 유저 나이와 성별은 "-"로 표시합니다.

- 로그는 hdfs에 저장되어 있고, 테이블은 mysql이며, Hive Table에 저장합니다.

 

예외 처리

- 웹 로그 데이터에는 유저 정보가 없는 항목이 있을 수 있습니다. (단, 유저ID는 반드시 존재)

- 테이블 A에는 이전 페이지 정보가 없을 수 있으니, 해당 항목이 없을 경우에는 Null 또는 None으로 처리합니다.

 

 

입력데이터

 

 

출력데이터 예시

 

 

풀이 코드

필요 라이브러리

pip install pandas pyhive sqlalchemy mysql-connector-python hdfs

 

python code

import pandas as pd
from sqlalchemy import create_engine
from pyhive import hive
from hdfs import InsecureClient

# MySQL 연결 설정 함수
def get_mysql_connection():
    engine = create_engine('mysql+mysqlconnector://<username>:<password>@<host>/<database>', echo=False)
    return engine

# Hive 연결 설정 함수
def get_hive_connection():
    conn = hive.Connection(host='<hive_host>', port=10000, username='<hive_username>', database='<hive_database>')
    return conn

# HDFS에서 웹 로그 데이터 읽기 함수
def read_web_log_from_hdfs(hdfs_path: str) -> pd.DataFrame:
    client = InsecureClient('http://<hdfs_host>:<port>', user='<hdfs_user>')
    with client.read(hdfs_path, encoding='utf-8') as reader:
        web_log = pd.read_csv(reader)
    return web_log
    
# 페이지 방문 통계 분석 함수
def analyze_user_visits(web_log: pd.DataFrame, table_a: pd.DataFrame, table_b: pd.DataFrame) -> pd.DataFrame:
    # 1. 웹 로그와 페이지 메타 정보를 조인 (페이지URL을 기준으로)
    merged_df = pd.merge(web_log, table_a, left_on='방문한페이지URL', right_on='페이지URL', how='left')

    # 2. 유저 정보와 병합 (유저ID를 기준으로)
    merged_df = pd.merge(merged_df, table_b, on='유저ID', how='left')

    # 3. 나이대 그룹화 (10년 단위로 그룹화, 나이가 없는 경우 '-')
    def age_grouping(age):
        if pd.isnull(age):
            return "-"
        age = int(age)
        if age < 10:
            return "0~10"
        elif age < 20:
            return "10~20"
        elif age < 30:
            return "20~30"
        elif age < 40:
            return "30~40"
        elif age < 50:
            return "40~50"
        else:
            return "50~"

    # 나이대 추가
    merged_df['유저나이그룹'] = merged_df['유저나이'].apply(age_grouping)

    # 4. 각 페이지별로 유저 방문 수 통계를 계산 (유저 성별과 나이대별 그룹화)
    grouped = merged_df.groupby(['페이지명', '페이지URL', '유저나이그룹', '유저성별']).size().reset_index(name='유저방문수')

    # 5. 유저 정보가 없는 경우 성별과 나이대를 "-"로 처리
    grouped['유저나이그룹'] = grouped['유저나이그룹'].fillna('-')
    grouped['유저성별'] = grouped['유저성별'].fillna('-')

    return grouped

# Hive 테이블에 데이터 저장 함수
def save_to_hive(data: pd.DataFrame, hive_table: str):
    conn = get_hive_connection()
    cursor = conn.cursor()

    # DataFrame의 데이터를 Hive 테이블에 삽입 (혹은 덮어쓰기)
    cursor.execute(f"DROP TABLE IF EXISTS {hive_table}")
    cursor.execute(f"CREATE TABLE {hive_table} (페이지명 STRING, 페이지URL STRING, 유저방문수 INT, 유저나이그룹 STRING, 유저성별 STRING)")

    # Hive에 데이터를 INSERT
    for index, row in data.iterrows():
        insert_query = f"INSERT INTO {hive_table} VALUES ('{row['페이지명']}', '{row['페이지URL']}', {row['유저방문수']}, '{row['유저나이그룹']}', '{row['유저성별']}')"
        cursor.execute(insert_query)
    
    conn.close()

# 메인 함수
def main(hdfs_path: str, hive_table: str):
    # MySQL 연결 설정
    mysql_engine = get_mysql_connection()

    # HDFS에서 웹 로그 읽기
    web_log = read_web_log_from_hdfs(hdfs_path)

    # MySQL에서 테이블 A, B 읽기
    table_a_query = "SELECT 페이지URL, 페이지명, `이전 페이지` FROM table_a"
    table_b_query = "SELECT 유저ID, 유저명, 유저나이, 유저성별 FROM table_b"

    table_a = pd.read_sql(table_a_query, con=mysql_engine)
    table_b = pd.read_sql(table_b_query, con=mysql_engine)

    # 페이지 방문 통계 분석
    result = analyze_user_visits(web_log, table_a, table_b)

    # Hive 테이블에 저장
    save_to_hive(result, hive_table)

if __name__ == "__main__":
    hdfs_path = "/user/hdfs/web_log.csv"
    hive_table = "page_visit_statistics"
    main(hdfs_path, hive_table)

 

설명

[ read_web_log_from_hdfs 함수 ]
# InsecureClient는 인증 없이 HDFS 클러스터에 연결할 때 사용됩니다.
client = InsecureClient('http://<hdfs_host>:<port>', user='<hdfs_user>')


[ analyze_user_visits 함수 ]
# merged_df에 log + tableA + tableB 를 join 합니다.
   유저ID  방문한페이지URL             방문시간   페이지URL    페이지명  이전 페이지  유저명  유저나이 유저성별
0  1001          /home  2024-01-01 12:00:00    /home      Home      None  Alice    25     F
1  1002          /home  2024-01-01 12:05:00    /home      Home      None    Bob    35     M
2  1003      /products  2024-01-01 12:10:00  /products  Products     /home Charlie    45     M

# pandas.apply를 적용하여 유저나이를 그룹화하여 치환합니다.
merged_df['유저나이그룹'] = merged_df['유저나이'].apply(age_grouping)

# size를 통해 그룹화된 행수를 집계하고 이를 '유저방문수'라는 새로운 컬럼으로 변환합니다.
grouped = merged_df.groupby(['페이지명', '페이지URL', '유저나이그룹', '유저성별']).size().reset_index(name='유저방문수')