이전 내용
[FastAPI] FastAPI + Streamlit + PostgresSQL (1)
이전 내용 [SQL] PostgreSQL: pg Admin 사용하기이전 내용 [SQL] PostgresSQL 다운로드 하기PostgresSQL 란? PostgreSQL은 오픈 소스 객체-관계형 데이터베이스 관리 시스템(ORDBMS)으로, 많은 기능과 뛰어난 성능, 유
puppy-foot-it.tistory.com
대략적인 진행 순서
- PostgreSQL (pg Admin)을 통해 데이터베이스, 테이블 생성
- FastAPI (VS Code)를 통해
- 가상 환경 만들기 (가상 환경명: my_shop)
- 가상 환경에 필요한 라이브러리 설치하기
- PostgreSQL 연결하기
- 가상 데이터(Faker 라이브러리) 생성
- 생성된 데이터 데이터베이스에 삽입하기 ▶ 1편
- 고객 더미 데이터, 마케팅 더미 데이터 수정 및 추가
- 데이터 분석 함수 만들기 ▶ 2편
- FastAPI로 API 기능 구현하기
- 포스트맨으로 테스트 하기
- Streamlit (VS Code) 을 통해
- Streamlit과 FastAPI 연동하기
- Streamlit 데이터분석 시각화 함수 구현하기
- Streamlit 실행하여 확인하기
고객 더미 데이터, 마케팅 더미 데이터 수정 및 추가
PostgreSQL pgAdmin을 켜고, customers 테이블에 데이터를 수정(또는 삽입)하고 marketing 테이블에 더미 데이터를 추가하는 작업을 한다.
-- 고객 더미 데이터 수정
update customers set name='김기태', userid='danielcoleman', passwd='1234' where customer_id=19;
update customers set name='김나라', userid='tracy95', passwd='1111' where customer_id=20;
update customers set name='김대철', userid='tbaker', passwd='2222' where customer_id=21;
update customers set name='유순복', userid='bryantcraig', passwd='3333' where customer_id=22;
update customers set name='박소영', userid='joanwhite', passwd='4444' where customer_id=23;
update customers set name='이초연', userid='blackwellbrandon', passwd='1212' where customer_id=24;
update customers set name='이정숙', userid='todd97', passwd='1212' where customer_id=25;
update customers set name='함창훈', userid='nmercer', passwd='1004' where customer_id=26;
update customers set name='변영미', userid='laura52', passwd='7979' where customer_id=27;
update customers set name='강수민', userid='jacobpena', passwd='1234' where customer_id=28;
update customers set name='김재우', userid='kevin17', passwd='1255' where customer_id=29;
update customers set name='신예은', userid='lovegarrett', passwd='2848' where customer_id=30;
update customers set name='서지혜', userid='elizabethfreeman', passwd='3434' where customer_id=31;
update customers set name='김계희', userid='jeffreybrown', passwd='2482' where customer_id=32;
update customers set name='장원', userid='akelly', passwd='5678' where customer_id=33;
update customers set name='김슬기', userid='carterjames', passwd='8765' where customer_id=34;
update customers set name='박영점', userid='charles58', passwd='2848' where customer_id=35;
update customers set name='신동진', userid='julie94', passwd='9876' where customer_id=36;
update customers set name='이민호', userid='mburke', passwd='2121' where customer_id=37;
update customers set name='전창우', userid='garciaronald', passwd='4343' where customer_id=38;
update customers set name='송미선', userid='uedwards', passwd='6565' where customer_id=39;
update customers set name='김미리내', userid='raymondpitts', passwd='1414' where customer_id=40;
update customers set name='김문기', userid='lewischad', passwd='8284' where customer_id=41;
update customers set name='조주용', userid='bergeranthony', passwd='2121' where customer_id=42;
update customers set name='윤정호', userid='jorge27', passwd='4248' where customer_id=43;
update customers set name='송기석', userid='markcoffey', passwd='6767' where customer_id=44;
update customers set name='박진성', userid='gregorymaria', passwd='4748' where customer_id=45;
update customers set name='장원영', userid='daniel64', passwd='9876' where customer_id=46;
update customers set name='김명은', userid='victorerickson', passwd='3235' where customer_id=47;
update customers set name='오미란', userid='marshalltyler', passwd='9797' where customer_id=48;
commit;
-- 마케팅 더미 데이터
-- 마케팅 테이블 더미 데이터 추가
INSERT INTO marketing (campaign_name, target_audience, budget, start_date, end_date)
VALUES
('여름 시즌 세일', '20대 여성', 5000000, '2024-06-01', '2024-06-30'),
('신제품 출시 이벤트', '30대 남성', 3000000, '2024-05-01', '2024-05-15'),
('가을 맞이 할인', '40대 이상', 4000000, '2024-09-01', '2024-09-30'),
('겨울 패션 프로모션', '20대 남성', 3500000, '2024-12-01', '2024-12-15'),
('블랙 프라이데이 특가', '30대 여성', 10000000, '2024-11-25', '2024-11-27'),
('핸드폰 액세서리 세일', '10대 남녀', 2000000, '2024-07-10', '2024-07-20'),
('할로윈 의상 할인', '10대 여성', 1500000, '2024-10-20', '2024-10-31'),
('봄맞이 대세 상품', '20대 남성', 2500000, '2024-04-01', '2024-04-15'),
('추석 특별 기획전', '40대 이상 여성', 3000000, '2024-09-01', '2024-09-10'),
('크리스마스 선물 이벤트', '20대 여성, 남성', 7000000, '2024-12-01', '2024-12-24'),
('재입고 알림 캠페인', '30대 여성', 1200000, '2024-08-10', '2024-08-20'),
('명절 선물 세트', '40대 남성', 3500000, '2024-08-25', '2024-09-05'),
('명품 할인 프로모션', '30대 남성', 8000000, '2024-07-01', '2024-07-15'),
('여름 휴가용 의류 세일', '20대 여성', 4500000, '2024-06-10', '2024-06-25'),
('새해 맞이 대할인', '20대 남성', 6000000, '2024-01-01', '2024-01-07'),
('스타일리시한 봄 의류 할인', '30대 여성', 3000000, '2024-04-10', '2024-04-25'),
('여름 나들이 패션', '20대 여성', 5000000, '2024-06-15', '2024-06-30'),
('겨울철 방한 용품 세일', '40대 이상 남성', 3500000, '2024-12-05', '2024-12-20'),
('겨울 스키 의류 세일', '20대 남성', 4500000, '2024-12-01', '2024-12-10'),
('봄 패션 트렌드 세일', '20대 여성', 2500000, '2024-03-01', '2024-03-15'),
('가을 레이어드 스타일', '30대 남성', 4000000, '2024-09-10', '2024-09-30'),
('피트니스 의류 할인', '30대 여성', 3000000, '2024-05-15', '2024-06-01'),
('가을 컬러 팔레트', '20대 남성', 3500000, '2024-10-01', '2024-10-15'),
('오래된 상품 재고 세일', '40대 이상', 2500000, '2024-08-15', '2024-08-31'),
('여성 명품 가방 세일', '20대 여성', 6000000, '2024-07-01', '2024-07-15'),
('겨울 홈 웨어 세일', '30대 여성', 4000000, '2024-12-10', '2024-12-20'),
('가정용 가전 세일', '40대 이상 남성', 5000000, '2024-11-01', '2024-11-15'),
('봄맞이 화장품 이벤트', '20대 여성', 2000000, '2024-04-05', '2024-04-20'),
('하계 스포츠 용품 세일', '10대 남녀', 3000000, '2024-06-01', '2024-06-15'),
('크리스마스 할인 이벤트', '30대 남성', 8000000, '2024-12-01', '2024-12-24'),
('신상품 출시 할인', '20대 여성', 4000000, '2024-05-01', '2024-05-15'),
('겨울철 필수 아이템 세일', '40대 이상', 3500000, '2024-12-01', '2024-12-10'),
('가을 신상 패션', '30대 여성', 5000000, '2024-09-01', '2024-09-15'),
('봄맞이 새로운 스타일', '20대 남성', 2500000, '2024-04-01', '2024-04-15'),
('주말 한정 특별 할인', '20대 여성, 남성', 3000000, '2024-07-10', '2024-07-12'),
('온라인 쇼핑몰 이벤트', '30대 남성', 6000000, '2024-10-01', '2024-10-10'),
('새해 첫 쇼핑 이벤트', '20대 여성', 4500000, '2024-01-01', '2024-01-07'),
('할로윈 기념 세일', '10대 남녀', 1500000, '2024-10-25', '2024-10-31'),
('여성 패션 아이템 세일', '30대 여성', 5000000, '2024-08-01', '2024-08-15'),
('추석 대세 상품', '40대 이상 남성', 3500000, '2024-09-05', '2024-09-10');
UPDATE sales s
SET marketing_id = (
SELECT m.marketing_id
FROM marketing m
WHERE s.sale_date::DATE BETWEEN m.start_date AND m.end_date
ORDER BY m.start_date ASC
LIMIT 1
)
WHERE EXISTS (
SELECT 1
FROM marketing m
WHERE s.sale_date::DATE BETWEEN m.start_date AND m.end_date
);
commit;
select * from customers;
select * from products;
select * from sales;
select * from marketing;
select * from payments;
select * from reviews;
마케팅 테이블을 확인해 보니, 데이터가 잘 삽입된 것을 확인할 수 있다.
데이터 분석 함수 만들기
이제 VSCode에서 파이썬 파일(analysis.py)을 하나 만들고, PostgreSQL 의 테이블과 연결한 뒤, 테이블을 불러와서 데이터 분석을 수행하는 함수를 만든다. 분석은 고객 / 판매 / 마케팅 영역을 분석할 것이며, 각 영역별 분석 관련 함수를 생성한다.
[필요한 라이브러리 import 및 데이터베이스 연결]
import pandas as pd
from sqlalchemy import create_engine
from datetime import datetime, timedelta
from sklearn.linear_model import LinearRegression
import numpy as np
import json
from pandas import Timestamp
import math
# SQLAlchemy(ORM 라이브러리) 데이터베이스 연결 설정
DATABASE_URL = "postgresql://postgres:1234@localhost:5432/shop"
engine = create_engine(DATABASE_URL)
[고객 분석 함수: customers 테이블, sales 테이블]
# 고객 분석 함수
def get_customer_analysis():
try:
# 고객 테이블과 판매 테이블 불러오기
customers_df = pd.read_sql("SELECT * FROM customers", engine)
sales_df = pd.read_sql("SELECT * FROM sales", engine)
# 성별 인원수, 총 거래 건수, 총 판매 금액 집계
gender_distribution = (
customers_df
.merge(sales_df, on='customer_id', how='left') # 고객과 판매 데이터 조인
.groupby('gender') # 성별을 기준으로 집계(agg)
.agg(
customer_count=('customer_id', 'nunique'), # 성별 고객 수
total_sales=('sale_id', 'count'), # 성별 총 거래 건수
total_revenue=('total_price', 'sum') # 성별 총 판매 금액
)
.reset_index()
)
# 회원별 구매 건수 및 금액 집계
purchase_data = sales_df.groupby(['customer_id']).agg(
purchase_count =('sale_id', 'size'),
total_amount=('total_price', 'sum')
).reset_index()
# 고객 정보와 결합
purchase_data = pd.merge(customers_df[['customer_id', 'name']], purchase_data, on='customer_id', how='left')
# 구매 건수 및 금액에 대한 통계 데이터 계산
purchase_statistics = purchase_data[['purchase_count', 'total_amount']].describe().to_dict()
# 최근 31일 동안 구매하지 않은 고객 조회
current_date = datetime.now()
thirty_one_days_ago = current_date - timedelta(days=31) # 기준일자: 현재일자 - 31일
last_purchase = sales_df.groupby('customer_id')['sale_date'].max().reset_index() # 고객 마지막 구매일자
last_purchase['sale_date'] = pd.to_datetime(last_purchase['sale_date'])
inactive_customers = pd.merge(customers_df[['customer_id', 'name', 'userid', 'email', 'phone']], last_purchase, on='customer_id', how='left')
# 구매 기록이 없거나, 마지막 구매일자가 기준일자보다 작음
inactive_customers = inactive_customers[inactive_customers['sale_date'].isna() | (inactive_customers['sale_date'] < thirty_one_days_ago)]
print("sales_df date range: ", sales_df['sale_date'].min(), sales_df['sale_date'].max()) # 최초 구매일자 ~ 마지막 구매일자
print("inactive_customers shape: ", inactive_customers.shape) # 31일 동안 구매하지 않은 고객수
# 상위 5명 고객 조회 (총 구매 금액 기준)
top5_customers = sales_df.groupby(['customer_id']).agg(
total_spent = ('total_price', 'sum')
).reset_index().sort_values('total_spent', ascending=False)
top5_customers = pd.merge(customers_df[['customer_id', 'name', 'userid', 'email', 'phone']], top5_customers, on='customer_id', how='left').head(5)
print("상위 5명 고객 조회(총 구매 금액 기준): ", top5_customers)
# 데이터 변환
results = {
"gender_distribution" : gender_distribution.to_dict(orient='records'),
"purchase_data" : purchase_data.to_dict(orient='records'),
"purchase_statistics" : purchase_statistics, # 통계 데이터
"inactive_customers" : inactive_customers.to_dict(orient='records'),
"top5_customers" : top5_customers.to_dict(orient='records'),
}
results = convert_timestamps(results)
return results
except Exception as e:
print(f"Error: {str(e)}")
raise Exception(f"An error occured while retrieving data: {str(e)}")
함수 기능
- 데이터 불러오기: 판매, 상품, 고객 데이터베이스로부터 데이터 추출.
- 판매 요약 집계: 상품별로 판매 금액 및 판매 수량을 집계하여 요약.
- 가장 많이 팔린 상품 및 안 팔린 상품: 판매 금액 기준으로 가장 많이 팔린 5개 상품과 가장 안 팔린 5개 상품 식별.
- 월별 판매 집계: 월별로 판매 금액을 집계하여, 월별 판매 현황 분석.
- 예상 판매 예측: 선형 회귀를 통해 향후 1년간의 판매를 예측.
- 결과 변환: 최종적으로 모든 결과를 JSON 형식으로 변환하고, 타임스탬프 및 기간 변환 함수를 호출하여 데이터 형식을 표준화.
- 결과 반환: 최종 분석 결과 반환.
[판매 데이터 분석: sales 테이블, products 테이블, customers 테이블]
◆ 예측 모델 (함수) 만들기
판매 데이터의 경우, 향후 1년 (12개월)의 월별 판매금액도 예측할 수 있는 예측 모델을 만들어 넣어준다.
예측 모델은 사이킷런의 선형회귀(LinearRegression) 으로 수행하며, get_sales_analysis() 함수에서 호출할 수 있도록, 예측 모델 관련 함수를 먼저 생성해 준다.
# 예측 모델
def def_months_predict(monthly_sales):
# 월별 판매 데이터를 기반으로 선형 회귀 예측
monthly_sales['month_num'] = np.arange(len(monthly_sales)) # 월 번호
# 선형 회귀 모델 적용
model = LinearRegression()
model.fit(monthly_sales['month_num'].values.reshape(-1, 1), monthly_sales['total_monthly_sales_amount'].values)
# 향후 12개월 예측
future_months = np.arange(len(monthly_sales), len(monthly_sales) + 12).reshape(-1, 1)
forecast_sales = model.predict(future_months)
# 예측 결과를 데이터프레임에 추가
future_dates = pd.date_range(start=monthly_sales['month'].max(), periods=13, freq='M')[1:]
forecast_df = pd.DataFrame({
'month': future_dates,
'predicted_sales': forecast_sales
})
return forecast_df
함수 기능
- 월 번호 부여: monthly_sales에 0부터 시작하는 월 번호(month_num)를 추가하여 선형 회귀 모델의 독립 변수로 사용.
- 선형 회귀 모델 적용: LinearRegression 객체를 생성하고, month_num과 total_monthly_sales_amount를 사용해 모델 학습.
- 향후 12개월 예측: 현재 데이터의 길이 후 12개월의 월 번호를 생성하고, 예측 판매 값 계산.
- 예측 결과 추가: 예측된 날짜와 판매량을 포함하는 데이터프레임(forecast_df)을 생성하여 반환.
◆ 판매 데이터 분석 함수 만들기
# 판매 분석
def get_sales_analysis():
try:
# 테이블에서 데이터 불러오기
sales_df = pd.read_sql("SELECT * FROM sales", engine)
products_df = pd.read_sql("SELECT * FROM products", engine)
customers_df = pd.read_sql("SELECT * FROM customers", engine)
# 상품별 판매금액 및 판매수량 집계
sales_summary = sales_df.groupby('product_id').agg(
total_sales_amount=('total_price', 'sum'),
total_sales_quantity=('quantity', 'sum')
).reset_index()
# 상품명 결합
sales_summary = pd.merge(sales_summary, products_df[['product_id', 'name']], on='product_id', how='left')
print("판매 집계: ", sales_summary)
# 가장 많이 팔린 상품 5개
top5_products = sales_summary.sort_values('total_sales_amount', ascending=False).head(5)
print("(판매액 기준) 가장 많이 팔린 상품:" , top5_products)
# 가장 안 팔린 상품 5개
bottom5_products = sales_summary.sort_values('total_sales_amount', ascending=True).head(5)
print("(판매액 기준) 가장 안 팔린 상품:" , bottom5_products)
# 월별 판매금액 집계: 판매 데이터 프레임에 월(month) 칼럼 추가 후 월별 집계
sales_df['sale_date'] = pd.to_datetime(sales_df['sale_date']) # sale_date를 datetime으로 형변환
sales_df['month'] = sales_df['sale_date'].dt.to_period('M') # 월 칼럼 추가
# monthlty_sales = sales_df.groupby('month')['total_price'].sum().reset_index()
monthly_sales = sales_df.groupby('month').agg(
total_monthly_sales_amount = ('total_price', 'sum')
).reset_index()
# 차후 1년간 월별 판매 예상 (선형 회귀 예측)
monthly_sales['month'] = monthly_sales['month'].dt.strftime('%Y-%m')
forecast_df = def_months_predict(monthly_sales)
forecast_df['month'] = pd.to_datetime(forecast_df['month'])
forecast_df['month'] = forecast_df['month'].dt.to_period('M')
print("예상 판매: ", forecast_df)
# 데이터 변환
results = {
"sales_summary" : sales_summary.to_dict(orient='records'),
"top5_products" : top5_products.to_dict(orient='records'),
"bottom5_products" : bottom5_products.to_dict(orient='records'),
"monthly_sales" : monthly_sales.to_dict(orient='records'),
"predict_sales" : forecast_df.to_dict(orient='records'),
}
results = convert_timestamps(results)
results = convert_periods(results)
return results
except Exception as e:
print(f"Error: {str(e)}")
raise Exception(f"An error occured while retrieving data: {str(e)}")
함수 기능
- 데이터 불러오기: 데이터베이스에서 판매(sales), 상품(products), 고객(customers) 데이터 로드.
- 판매 집계: 상품별로 판매금액과 판매수량을 집계하여 sales_summary 데이터프레임을 생성하고, 상품명과 결합하여 판매 요약 출력.
- 판매 성과 분석: 판매액 기준으로 가장 많이 팔린 상품 5개 및 가장 안 팔린 상품 5개를 추출하여 출력.
- 월별 판매금액 집계: 월별 판매금액을 집계하고, 월 정보를 추가하여 monthly_sales 데이터프레임 생성.
- 판매 예측: 월별 판매 데이터를 기반으로 향후 1년간의 판매를 선형 회귀 예측을 통해 계산하고, 예측된 결과 출력.
- 결과 변환 및 반환: 분석 결과를 딕셔너리 형태로 변환 후 시간 및 기간 정보를 적절히 변환하여 결과 반환.
[마케팅 데이터 분석: makerting 테이블, sales 테이블, customers 테이블, products 테이블]
# 마케팅 분석
def get_marketing_analysis():
try:
# 테이블에서 데이터 불러오기
campaigns_df = pd.read_sql("SELECT * FROM marketing", engine)
sales_df = pd.read_sql("SELECT * FROM sales", engine)
customers_df = pd.read_sql("SELECT * FROM customers", engine)
products_df = pd.read_sql("SELECT * FROM products", engine)
print(campaigns_df.columns) # campaign_df의 모든 칼럼 출력
# 날짜 변환
sales_df['sale_date'] = pd.to_datetime(sales_df['sale_date']).dt.date
# 캠페인별 판매 분석: marketing_id로 그룹하고 total_price, quantity 합 집계
campaign_sales = sales_df.groupby('marketing_id').agg(
total_sales_amount=('total_price', 'sum'),
total_sales_quantity=('quantity', 'sum')
).reset_index()
campaign_sales = pd.merge(campaign_sales, campaigns_df[['marketing_id', 'campaign_name']], on='marketing_id', how='left')
# 캠페인별 참여 고객 수 (마케팅 ID 를 통해 참여한 고객 수)
campaign_participants = sales_df.groupby('marketing_id')['customer_id'].count().reset_index()
campaign_participants = pd.merge(campaign_participants, campaigns_df[['marketing_id', 'campaign_name']], on='marketing_id', how='left')
campaign_participants.rename(columns={'customer_id': 'participant_count'}, inplace=True)
# 캠페인별 ROI 분석 * ROI = (판매금액 - 마케팅 비용) / 마케팅 비용)
campaigns_df = pd.merge(campaigns_df, campaign_sales[['marketing_id', 'total_sales_amount']], on='marketing_id', how='left')
campaigns_df['ROI'] = ((campaigns_df['total_sales_amount'] - campaigns_df['budget']) / campaigns_df['budget']) * 100
roi_analysis = campaigns_df[['marketing_id', 'total_sales_amount', 'ROI']]
# 상품별 캠페인 효과 분석
product_campaign_sales = sales_df.groupby(['marketing_id', 'product_id']).agg(
total_sales_amount = ('total_price', 'sum'),
total_sales_quantity = ('quantity', 'sum')
).reset_index()
product_campaign_sales = pd.merge(product_campaign_sales,
products_df[['product_id', 'name']], on='product_id', how='left')
product_campaign_sales = pd.merge(product_campaign_sales,
campaigns_df[['marketing_id', 'campaign_name']], on='marketing_id', how='left')
# 고객 세그먼트별 마케팅 분석 (고객 연령별)
customers_df['age'] = customers_df['birth_date'].apply(lambda x: (datetime.now() - pd.to_datetime(x)).days // 365) # 나이 계산
customers_df['age_group'] = pd.cut(customers_df['age'], bins=[0, 18, 30, 40, 50, 60, 100],
labels=['18이하', '19-30', '31-40', '41-50', '51-60', '60 이상'])
segment_sales = sales_df.groupby(['marketing_id', 'customer_id']).agg(
total_sales_amount = ('total_price', 'sum')
).reset_index()
segment_sales = pd.merge(segment_sales, customers_df[['customer_id', 'age_group']], on='customer_id', how='left')
segment_sales = pd.merge(segment_sales, campaigns_df[['marketing_id', 'campaign_name']], on='marketing_id', how='left')
segment_sales = segment_sales.groupby(['campaign_name', 'age_group']).agg(
total_sales_amount = ('total_sales_amount', 'sum')
).reset_index()
# 데이터 변환
results = {
"campaign_sales" : campaign_sales.to_dict(orient='records'),
"campaign_participants" : campaign_participants.to_dict(orient='records'),
"roi_analysis" : roi_analysis.to_dict(orient='records'),
"product_campaign_sales" : product_campaign_sales.to_dict(orient='records'),
"segment_sales" : segment_sales.to_dict(orient='records'),
}
results = convert_timestamps(results)
results = handle_float_values(results)
return results
except Exception as e:
print(f"Error: {str(e)}")
raise Exception(f"An error occured while retrieving data: {str(e)}")
함수 기능
- 데이터 불러오기: 마케팅(campaigns), 판매(sales), 고객(customers), 상품(products) 테이블에서 데이터 로드.
- 판매 분석: 캠페인별로 그룹화하여 판매금액 총합(total_sales_amount)과 판매수량 총합(total_sales_quantity)을 집계하고, 캠페인명과 결합하여 캠페인별 판매 분석 결과 생성.
- 참여 고객 수 집계: 각 캠페인별로 참여한 고객 수를 계산하고, 캠페인명과 결합.
- ROI 분석: 캠페인별 ROI(투자 수익률)를 계산하여 판매금액과 마케팅 비용 비교.
- 상품별 캠페인 효과 분석: 제품별로 캠페인에 따른 판매 효과를 분석하고, 제품명과 캠페인명 결합.
- 고객 세그먼트 분석: 고객의 연령을 계산하고 세그먼트별로 그룹화하여 마케팅 효과 분석.
- 결과 변환 및 반환: 각 분석 결과를 딕셔너리 형태로 변환하여 반환하며, 필요에 따라 데이터 형식 처리.
◆ 부동 소수점 처리 함수 생성
그리고 마케팅 데이터의 경우, JSON 직렬화 과정에서 NaN, 무한대(inf), 음의 무한대(-inf)와 같은 비정상적인 부동 소수점 (float)D이 발생할 수 있으므로, 이를 처리하는 handle_float_values() 함수를 생성해 준다.
# JSON 직렬화 과정에서 NaN, 무한대(inf), 음의 무한대(-inf)와 같은
# 비정상적인 부동 소수점 (float) 처리
def handle_float_values(obj):
if isinstance(obj, float):
if math.isnan(obj): # NaN 처리
return None
elif math.isinf(obj): # inf 처리
return None
else:
return obj
elif isinstance(obj, list): # 리스트 순회
return [handle_float_values(item) for item in obj]
elif isinstance(obj, dict): # 딕셔너리 순회
return {key: handle_float_values(value) for key, value in obj.items()}
else:
return obj
- NaN (Not a Number): 부동 소수점 연산에서 정의되지 않은 값이나 결과를 나타내는 특별한 값. 사용자가 NaN 값을 만나면, 이를 None으로 변환하여 데이터를 더 깔끔하게 처리할 수 있도록 한다.
- 무한대 (Infinity): 수학적으로 무한대에 해당하는 값을 처리하며, 이 역시 None으로 변환된다.
- 리스트 및 딕셔너리를 지원하여, 입력된 객체가 리스트나 딕셔너리인 경우 그 안의 요소를 재귀적으로 순회하며 같은 처리를 수행한다. 이로 인해 복잡한 데이터 구조에서도 모든 부동 소수점 값을 일관되게 처리할 수 있다.
- 처리할 수 없는 타입의 경우, 해당 객체를 그대로 반환한다. 이는 입력된 데이터의 일관성을 유지하는 데 도움을 준다.
[시간 및 날짜 데이터를 변환하는 기능을 수행하는 함수 생성]
반환되는 데이터에 datetime 객체나 Period 객체가 남아 있는 경우, 이를 사용하는 후속 작업에서 TypeError와 같은 예외가 발생할 수 있기 때문에 데이터 형식을 변환해 줘야 한다.
만약 convert_timestamps()와 convert_periods() 함수를 사용하지 않으면, 반환되는 데이터 내에서 날짜와 시간 형식이 다양하게 존재할 수 있어 이로 인해 데이터 처리나 시각화 과정에서 오류가 발생할 수 있다.
def convert_timestamps(obj, format='%Y-%m-%d %H:%M:%S'):
# Timestamp 또는 datetime 객체 처리
if isinstance(obj, (Timestamp, datetime)):
return obj.strftime(format)
# 리스트 처리
elif isinstance(obj, list):
return [convert_timestamps(item, format) for item in obj]
# 딕셔너리 처리
elif isinstance(obj, dict):
return {key: convert_timestamps(value, format) for key, value in obj.items()}
# 그외 객체는 그대로 반환
else:
return obj
# Period 객체를 문자열로 변환하는 함수
def convert_periods(obj):
if isinstance(obj, pd.Period):
return obj.strftime('%Y-%m')
elif isinstance(obj, list):
return [convert_periods(item) for item in obj]
elif isinstance(obj, dict):
return {key: convert_periods(value) for key, value in obj.items()}
else:
return obj
1. convert_timestamps()
주어진 객체를 처리하여 타임스탬프(Timestamp) 또는 날짜와 시간(datetime) 객체를 특정 형식의 문자열로 변환한다.
- Timestamp 또는 datetime 객체인 경우, 해당 형식으로 문자열로 변환.
- 0리스트일 경우, 리스트의 각 원소에 대해 동일한 변환을 재귀적으로 수행.
- 딕셔너리일 경우, 각각의 키와 값에 대해서도 동일하게 변환.
- 그 외의 타입일 경우, 객체를 그대로 반환.
2. convert_periods()
Pandas의 Period 객체를 연도와 월 형식의 문자열로 변환하는 기능을 한다.
- pd.Period 객체인 경우, 'YYYY-MM' 형식으로 변환하여 반환.
- 리스트인 경우, 리스트의 각 원소에 대해 동일한 변환을 재귀적으로 수행.
- 딕셔너리인 경우, 각각의 키와 값에 대해 동일하게 변환.
- 그 외의 타입일 경우, 객체를 그대로 반환.
다음 내용
[FastAPI] FastAPI + Streamlit + PostgresSQL (3)
이전 내용 [FastAPI] FastAPI + Streamlit + PostgresSQL (2)이전 내용 [FastAPI] FastAPI + Streamlit + PostgresSQL (1)이전 내용 [SQL] PostgreSQL: pg Admin 사용하기이전 내용 [SQL] PostgresSQL 다운로드 하기PostgresSQL 란? PostgreSQL은
puppy-foot-it.tistory.com
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI - 정적 파일, API Router (0) | 2025.04.27 |
---|---|
[FastAPI] FastAPI + Streamlit + PostgresSQL (3) (0) | 2025.04.26 |
[FastAPI] FastAPI + Streamlit + PostgresSQL (1) (1) | 2025.04.25 |
[파이썬] 프로젝트 : 웹 페이지 구축 - 10(Cloud에 배포하기) (0) | 2025.03.24 |
[파이썬] 프로젝트 : 웹 페이지 구축 - 9 (멀티페이지) (0) | 2025.03.24 |