프로젝트/관련 스터디

[돈독한 가계부_02] 템플릿에 삽입할 더미 데이터(dummy data) 생성하기

단ee 2024. 11. 7. 17:32

 

공유용 템플릿에 넣을 가짜 데이터가 필요했다.
왜냐고? 그래야 차트를 보여줄 수 있으니까.

 

 

초기 버전에서는 그냥 내 수입내역이랑 지출내역을 엑셀 내보내기 해서 만들었는데,
나중에 불특정 다수를 대상으로 공유할 때 이 내역을 그대로 사용할 수 없었다.

 

하지만 그렇다고 엑셀에서 무작위로 500여개 정도의 행을 일일이 복사+붙여넣기 해가며 만들 수도 없는 노릇.
단순 작업은 컴퓨터를 시켜보자는 마음으로 서치를 시작했다.

 

 

00. 더미 데이터?


정보과학(Infomatics)에서 더미 데이터란, 실제 유용한 데이터를 포함하지 않지만, 실제 데이터가 존재해야 할 공간을 채워주는 데이터이다.
실제 데이터를 사용하지 않고도 프로그램이 예상대로 작동하는지 테스트를 하기 위해 활용한다.\

 

 

 

01. 생성형 AI한테 시켜보자(feat. Gemini)


절대 날로 먹으려는 것이 아니다. 프롬프트 잘 작성하는 것도 능력이랬다...

 

 

하지만 제미나이는 결과를 출력하다가 뻗어버렸고... 대신 길게 요청한 내용을 파이썬 코드로 구현해주었다.

 

따라서 생성형 AI 시키기는 실패.

 

 

 

02. 코딩이 답인 것 같다. 할 일을 단계별로 쪼개보자


역시 돌고 돌아 파이썬.


예문 코드를 보면서 내가 필요한 모듈과 함수를 적절히 섞어가며 코드를 작성했다.
처음보는 메서드가 있어서 조금 헷갈리고 시간이 걸렸지만, 그래도 결국 완성할 수 있었다.

 

내게 필요한 건 크게 두 가지. '지출 내역'과 '수입 내역' 이다.

 

 

지출 내역의 경우,


  1. 일정 기간 사이의 날짜를 랜덤하게 골라 생성하고
  2. 이를 데이터프레임으로 만들어
  3. 범주형인 컬럼에 대해 주어진 선택지 내에서 랜덤하게 하나를 골라 출력하게 하고,
  4. 만약 특정 범주형에 종속되는 다른 범주형 컬럼이 있다면 3에서 출력된 결과에 따라 맞는 범주형을 선택하게 하고
  5. 일정 범위의 금액 사이에서 랜덤하게 추출하면 끝

 

 

수입 내역의 경우,


  1. 주기적으로 발생되는 고정 수입에 맞춰, 날짜는 같지만 월+1 이 되는 날짜를 생성하고
  2. 이를 데이터프레임으로 만들어
  3. 범주형 컬럼에 대해 지출 내역처럼 랜덤하게 생성하고,
  4. 비정기적인 수입 내역에 대해 날짜 몇 개를 랜덤하게 생성하면 끝

 

 

 

03. 파이썬으로 코딩해서 csv 파일을 만들자


1. 사용한 모듈

# import 
import pandas as pd 
import numpy as np 
import random from datetime 
import datetime, timedelta 

# 월(month)은 timedelta의 파라미터가 아니기 때문에, datetime이 아닌 다른 모듈 사용 
from dateutil.relativedelta import relativedelta

 

 

2. 랜덤 날짜 생성하기

  • 날짜는 1월 1일부터, 프로젝트 종료일인 10월 31일 사이의 값을 랜덤으로 추출하기로 했다.
  • 먼저 시작 날짜와 종료 날짜를 변수에 담고, 이 사이 일수인 304일 범위에서 랜덤하게 숫자를 하나 골라 시작 날짜에 더한 값을 리스트에 저장한다.
# 랜덤 날짜 생성 : Between 2024-01-01 and 2024-10-31 
start_date = datetime(2024, 1, 1) 
end_date = datetime(2024, 10, 31) 
num_days = (end_date - start_date).days 

dates = [] 
for i in range(550): # 550 개의 랜덤한 날짜 생성 
    random_days = random.randrange(num_days) 
    dates.append(start_date + timedelta(days=random_days))

 

3. 랜덤 날짜가 담긴 리스트 기준으로 데이터프레임 생성

# 지출 데이터프레임 생성 
df_expenses = pd.DataFrame({'결제일시': dates}) 
df_expenses['결제일시'] = pd.to_datetime(df_expenses['결제일시']).dt.strftime('%Y-%m-%d')

 

 

 

4. 범주형 컬럼에서 랜덤 생성하기

  • 예시 코드는 지출 상위 카테고리. 공유용 템플릿에 설정한 기본 카테고리들을 리스트에 담고, 그 중 하나를 랜덤으로 고른다.
# 상위 카테고리 랜덤 추가 
categories = ['통신요금', '주거관리비', '공과금', '보험료', '구독서비스'
	, '사회생활비', '여행', '식비', '생필품비', '교통유류비', '의류미용비'
    , '문화생활비', '건강관리비', '교육비', '생활서비스비', '저축', '투자'] 
df_expenses['상위_카테고리'] = np.random.choice(categories, size=len(df_expenses))

 

 

 

5. 종속되는 하위 범주형 컬럼 랜덤 생성하기

  • 각각의 지출 상위 카테고리에는 해당되는 지출 하위 카테고리가 존재한다. 예를 들어, '공과금' 카테고리 아래에는 '전기요금', '수도요금', '가스요금' 등이 있다.
  • 특정 행에 대해, '상위_카테고리'의 값이 딕셔너리의 key 에 해당하면 value 중에서 랜덤으로 하나를 출력하게 하는 함수를 사용하여 데이터프레임에 적용한다. value 값이 리스트가 아닌 하나의 텍스트라면 그것만 출력하게 한다.
# 하위 카테고리 매칭을 위한 딕셔너리와 함수 정의
sub_category_dict = {
    '통신요금' : ['휴대전화', '인터넷']
    , '주거관리비' : ['월세', '보증금/대출이자', '관리비']
    , '공과금' : ['전기요금', '수도요금', '가스요금']
    , '보험료' : ['실손보험']
    , '구독서비스' : ['서비스구독', 'OTT구독']
    , '사회생활비' : ['경조사비', '선물', '모임회비']
    , '여행' : ['숙소', '교통', '식비/간식/카페', '액티비티', '쇼핑', '기타여행경비']
    , '식비' : ['커피/음료', '외식', '간식', '식재료', '기타식비']
    , '생필품비' : ['생활용품', '가구/가전', '소모품', '기타생필품']
    , '교통유류비' : ['대중교통', '택시비', '기타교통비']
    , '의류미용비' : ['의류', '미용관리비', '기타미용비']
    , '문화생활비' : ['체험', '전시/관람', '취미교육', '재료구입', '도서', '기타문화비']
    , '건강관리비' : ['진료비', '약제비', '시설이용료', '운동강습', '기타건강관리비']
    , '교육비' : ['학습과정', '교재', '학습공간', '시험응시료', '기타교육비']
    , '생활서비스비' : ['생활서비스']
    , '저축' : ['보통예금', '적금', '주택청약저축']
    , '투자' : ['주식', '펀드', '채권', '기타투자']
}

def map_subcategory(row):
    value = sub_category_dict.get(row['상위_카테고리'])
    if isinstance(value, list):  # 값이 리스트인 경우
        return random.choice(value)  # 리스트에서 랜덤하게 값 선택
    else:
        return value
    
df_expenses['하위_카테고리'] = df_expenses.apply(map_subcategory, axis=1)

 

 

 

6. 결제 금액 랜덤 생성하기

  • 결제 금액은 1,000원에서 200,000원 사이의 범위로 제한하고, 십의 자리에서 반올림하여 백원 이상의 금액만 출력되도록 했다.
# 결제금액 랜덤 생성 
amounts = np.random.lognormal(mean=np.log(15000), sigma=1, size=len(df_expenses)) 
amounts = np.clip(amounts, 1000, 200000).astype(int) 
amounts = np.around(amounts, decimals=-2) 
df_expenses['금액'] = amounts

 

 

 

7. 수입 내역 만들기 : 월급일을 만들어보자

  • 근로수입 중 고정 수입인 월급은 해당 월의 특정 날짜에만 들어오므로, 시작일을 기준으로 1개월씩 증가하는 리스트를 만들어준다.
  • 또한 월급은 카테고리와 금액이 변경되지 않으므로, 삽입될 내용도 일괄적으로 만들어준다.
  • 단, 앞서 사용한 timedelta는 파라미터로 day만 지원한다. 2월(28일)의 경우도 있고, 각 일수가 31일, 30일 이렇게 변하므로 days=30으로 일괄 지정할 수 없다. 따라서 month를 지원하는 relativedelta를 사용했다.
# 월(month)은 timedelta의 파라미터가 아니기 때문에, 다른 모듈 사용
from dateutil.relativedelta import relativedelta

# 1. 고정 수입 데이터 만들기

# 날짜 범위 만들기
start_date_fix = datetime(2024, 1, 25)
dates = []
for i in range(10):
    dates.append(start_date_fix + relativedelta(months = i))

# DataFrame 생성
df_income_regular = pd.DataFrame({'일시': dates})
df_income_regular['일시'] = pd.to_datetime(df_income_regular['일시']).dt.strftime('%Y-%m-%d')

# 고정 금액 추가하기
df_income_regular['수입경로'] = '돈독은행'
df_income_regular['내역'] = '급여이체'
df_income_regular['금액'] = 2500000
df_income_regular['상위_카테고리'] = '근로소득'
df_income_regular['하위_카테고리'] = '급여'
df_income_regular['수입_카테고리'] = 'Fixed'

 

 

 

 

04. 결과물 & 고찰


위 코드로 만든 결과물을 .to_csv 메서드로 출력하면 완료!

 

 

 

 

내역 시트에 이렇게 넣으면 이렇게 차트로 확인할 수 있다!

 

 

 

 

금액 범위나 빈도 등을 조절해서 좀 더 사실적인 더미 데이터로 만들고 싶었는데,

일단 완성하는 게 먼저라 대충 공간만 채울 가상의 데이터를 만들게 되었다.

 

데이터프레임 형태로 상위카테고리-하위카테고리 매칭되는 코드를 써두었다면 위의 함수 쓰는 과정이 없었겠으나...

 

작업을 구글 시트로 한 탓에 하나씩 한땀한땀 옮겨 적어가며 만들기엔 너무 중노동이었다.


그렇다고 제미나이에게 시키려 했더니 csv로 가공해서 주거나 해야 인식하고...

 

그래서 그냥 아래 사진처럼 쿼리랑 함수를 적절히 섞어서 딕셔너리 형태로 구글 시트에서 가공했다. 역시 엑셀

 

 

그냥 매크로를 짜거나 Apps Script 짜도 됐을 것 같다는 생각이... 다 끝나고 들지만

 

그래도 오랜만에(?) 구글 시트와 엑셀을 벗어나 파이썬 코딩을 해서 재미졌다.