파이썬 예제 (엑셀 파일 읽기, 차트)

개요

이번에 만든 주제는 기상청에서 제공하는 기상 자료 개방 포털에서 다운 받은 날씨 데이터 엑셀 파일을 읽어 들여, 값을 보여주고 챠트로 만들어본 예제 입니다.

파이썬 언어로 만들어져 있으며 아래 패키지, 모듈을 활용해 구성하였습니다.

PyQt5 + Pandas + Matplotlib


기상청 날씨 자료는 부산, 제주도의 2020년 1월 1일 ~ 1월 5일까지 데이터 이며,
매 시간별 기온, 강수량, 풍속, 습도가 기록되어 있습니다.

샘플 엑셀 파일

프로그램을 실행하면 아래와 같이 메인 화면이 보이고, 여기서 엑셀 파일 열기 버튼을 선택해 해당파일을 로드 합니다.



선택된 엑셀 파일은 pandasDataframe으로 읽어들인 후, PyQt5QTableWidget에 아래와 같이 표시됩니다.

엑셀 파일의 워크시트가 2개 (부산, 제주도) 이므로, 각각 QComboBox에 시트 이름이 추가되어 변경시 각 시트를 변경해 보여줍니다.


챠트 보기 버튼을 선택하면 해당 지역의 기상 데이터를 pandas에서 제공하는 matplotlib와 연동해 챠트로 보여주는 기능을 넣어 보았습니다.



소스코드


from PyQt5.QtWidgets import (QApplication, QWidget, QTableWidget, QTableWidgetItem,
QVBoxLayout, QHBoxLayout, QPushButton, QComboBox, QFileDialog)
from PyQt5.QtCore import Qt
import sys
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.font_manager as fm
import os

# 한글 폰트 적용
path = 'C:/Windows/Fonts/malgun.ttf'
font_name = fm.FontProperties(fname=path, size=12).get_name()
plt.rc('font', family=font_name)

QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)

class myWindow(QWidget):

def __init__(self):
super().__init__()
self.resize(500,600) # 위젯 사이즈

# List for Pandas Dataframes
self.df_list = []

self.cmb = QComboBox(self)
open_btn = QPushButton('엑셀 파일 열기', self)
chart_btn = QPushButton('챠트 보기', self)

# 수평 박스 배치
hbox = QHBoxLayout()
hbox.addWidget(self.cmb)
hbox.addWidget(open_btn)
hbox.addWidget(chart_btn)

self.table = QTableWidget(self)

# 수직 박스 배치
vbox = QVBoxLayout()
vbox.addLayout(hbox)
vbox.addWidget(self.table)
self.setLayout(vbox)

# 시그널 연결
open_btn.clicked.connect(self.clickOpenBtn)
chart_btn.clicked.connect(self.clickChartBtn)
self.cmb.currentIndexChanged[int].connect(self.cmbChanged)

def clickOpenBtn(self):
file_path, ext = QFileDialog.getOpenFileName(self, '파일 열기', os.getcwd(), 'excel file (*.xls *.xlsx)')
if file_path:
self.df_list = self.loadData(file_path)

# 콤보박스 워크시트 목록 추가
for i in self.df_list:
self.cmb.addItem(i.name)

self.initTableWidget(0)

def clickChartBtn(self):
# 현재 선택된 Dataframe id
id = self.cmb.currentIndex()
name = self.cmb.currentText()
if id>-1:
df = self.df_list[id].copy()
if not df.empty:
df.set_index(df.columns[0], drop=True, inplace=True)
df.plot()
plt.title(f'기상청 날씨-{name}')
plt.show()

def cmbChanged(self, id):
self.initTableWidget(id)

def loadData(self, file_name):
df_list = []
with pd.ExcelFile(file_name) as wb:
for i, sn in enumerate(wb.sheet_names):
try:
df = pd.read_excel(wb, sheet_name=sn)
except Exception as e:
print('File read error:', e)
else:
df = df.fillna(0)
df.name = sn
df_list.append(df)
return df_list

def initTableWidget(self, id):
# 테이블 위젯 값 쓰기
self.table.clear()
# select dataframe
df = self.df_list[id];
# table write
col = len(df.keys())
self.table.setColumnCount(col)
self.table.setHorizontalHeaderLabels(df.keys())

row = len(df.index)
self.table.setRowCount(row)
self.writeTableWidget(id, df, row, col)

def writeTableWidget(self, id, df, row, col):
for r in range(row):
for c in range(col):
item = QTableWidgetItem(str(df.iloc[r][c]))
self.table.setItem(r, c, item)
self.table.resizeColumnsToContents()

if __name__ == '__main__':
app = QApplication(sys.argv)
w = myWindow()
w.show()
sys.exit(app.exec_())
1~9번 라인은 필요한 파이썬 패키지를 불러오는 코드입니다.

11~14번 라인은 matplotlib로 챠트를 그릴 때 한글폰트가 깨지는 현상을 방지하기 위해, 챠트 기본 폰트를 맑은 고딕폰트로 폰트를 변경하는 부분입니다.

이어서 myWindow라는 class (QWidget에서 상속) 관련 코드입니다. 이 클래스가 실제 보여지는 화면 위젯을 담당합니다.

20~48 번 라인의 __init__() 생성자 함수는 QCombox, QTableWidget, QPushButton 등 컨트롤을 생성하고, QHBoxLayout class를 통해 컨트롤을 화면 위젯에 배치합니다.

그리고 컨트롤(버튼, 콤보박스 등)과 연동될 시그널을 슬롯함수와 연결합니다.

즉, 아래 코드는 open_btn(버튼 객체)을 클릭하면 클래스의 method인 self.clickOpenBtn() 함수를 호출한다는 뜻입니다.
open_btn.clicked.connect(self.clickOpenBtn)
이제 생성자 함수 호출이 끝나면 위젯 객체는 아래와 같은 모습으로 초기화 됩니다.

myWindow Class

50~59번 라인은 위젯에서 "엑셀 파일 열기" 버튼을 클릭했을때 호출되는 함수이며, QFileDialog class를 이용해 파일 열기 상자를 모달로 생성하고 사용자가 선택한 파일을 경로와 확장자 형태의 튜플로 리턴 받습니다.

QFileDialog

파일을 선택하면 loadData() 함수를 이용해 pandas Dataframe으로 읽어 들인 후, self.df_list 라는 리스트로 저장하게 됩니다.

DataFrame을 리스트로 저장하는 이유는 날씨 정보가 담긴 엑셀 파일의 워크시트가 여러개 일 수 있기 때문입니다.

워크시트 2개(부산, 제주도)

만약 Dataframe에 익숙하지 않다면 Pandas 를 반드시 공부해보기 바랍니다.

파이썬을 공부한다면 Pandas, Numpy, Matplotlib는 피해갈 수 없는 숙명과도 같습니다.
공부해보면 피하기 보다는, 너무 고마운 존재라는 것을 깨닫게 될 것입니다.

STL이 없는 C++을 상상할 수 없듯이 위 3대장이 빠진 파이썬을 상상할 수 없습니다.

이어서 76~88번 라인의 loadData() 메서드는 앞서 설명한 엑셀 파일로부터 Pandas Dataframe을 생성합니다.

Pandas의 Dataframe class는 한마디로 2차원 배열이라고 생각하면 됩니다. 불러들인 엑셀 데이터를 보이는 그대로 2차원(행, 열을 갖는 스프레드시트)  데이터 타입으로 만들어 줍니다.

pd.ExcelFile class를 이용해 엑셀 파일을 Open하고 wb(워크북) 이라는 객체로 저장한 후, 해당 워크북의 워크시트 수만큼 반복을 이용해 pd.read_excel() 함수로 워크시트를 불러들입니다.

여기서 워크시트별로 생성된 Dataframe(df)을 self.df_list 리스트 변수에 저장해 둡니다.

85번 라인의 df.fillna(0)  함수는 엑셀파일 중 강수량 데이터가 비가 오지 않아 공백으로 처리되어 있는데 이를 Dataframe에서 "nan" 타입으로 인식하는 것을 방지하기 위해 0으로 대체하는 함수입니다.

90~102번 라인 initTableWidget() 메서드는 위젯에 배치된 QTableWidget로 엑셀에서 읽어들인 Dataframe 데이터를 테이블에 시각화 합니다.

QTableWidget Class

df.keys() 함수를 통해 엑셀에서 불러들인 Dataframe Column(열) 의 개수를 파악하고, df.index를 이용해 Row(행) 의 개수를 파악합니다.

이제 QTableWidget의 행과, 열 개수를 setColumnCount(), setRowCount() 함수를  통해 초기화 한 후, writeTableWidget() 메서드를 호출합니다.

104~109번 라인 writeTableWidget() 함수는 전달인자로 받은 워크시트 번호와 df, row, col을 이용해 행과 열의 수만큼 2중 for문을 돌며 내용을 추가해 사용자에게 보여지도록 합니다.

마지막으로 챠트를 그리는 부분은 61~71번 라인 clickChartBtn() 함수가 담당하며, "챠트 보기" 버튼을 클릭하면 수행되는 시그널의 슬롯함수 입니다.

고맙게도 Pandas Dataframe의 plot() 함수는 2차원 배열 형태로 저장된 Dataframe을 바로 차트로 시각화 시켜줍니다.


이때 Matplotlib가 활용됩니다.

다만 차트의 Y축에 그릴 필요가 없는 Dataframe의 시간 데이터(엑셀 0번 컬럼 : "일시" )는 X축 데이터로 전환 시킬 필요가 있습니다.
df.set_index(df.columns[0], drop=True, inplace=True) 
위 함수가 그 동작을 수행하며, 0번 컬럼인 시간 데이터를 X축 인덱스로 설정하고 drop 전달인자를 True로 설정하면 DataFrame 에서 제거 됩니다.

set_index() 함수

111~115번 라인은 Main 함수이며, QApplicationQWidget 객체를 생성하는 프로그램의 시작점입니다.

이상으로 설명을  마칩니다.

  • 개발환경 : Windows 10 Pro, VS 2017, Python 3.7
  • 사용 모듈 : PyQt5(5.14.2), Pandas(1.0.1), Matplotlib(3.2.0)
  • Pyinstaller 실행 파일 : Data_Analysis
  • 샘플 엑셀 파일 : 기상청 날씨.xlsx

댓글

  1. 독학중인데 많은 도움 받고 있습니다. 질문있는데요..csv파일을 TableWidget에 출력하려 하는데 잘 안되네요.
    def clickOpenBtn(self):
    file_path, ext = QFileDialog.getOpenFileName(self, '파일 열기', os.getcwd(), 'excel file (*.csv)')

    답글삭제
    답글
    1. 안녕하세요.

      QFileDialog를 이용해 파일의 경로를 얻은 후, 아래 2가지 방법 중 편리한 방법을 선택해 *.csv를 열면 됩니다.

      1. Python 내장 모듈 csv 를 이용한 방법.
      (https://docs.python.org/ko/3/library/csv.html#module-csv)

      2. Python 외장 모듈 Pandas를 이용한 방법.
      (https://pandas.pydata.org/docs/user_guide/io.html#io-read-csv-table)

      csv 파일을 읽은 후, QTableWidget에 쓰는 방법은 블로그 게시물 중 "COVID-19 시도별 현황" 편을 참조 바랍니다.

      삭제

댓글 쓰기

이 블로그의 인기 게시물

Qt Designer 설치하기

C++ 예제 (소켓 서버, 이미지, 파일전송)