반응형

라즈베리파이 강좌 21강 | 데이터 분석(엑셀/파이썬) – CSV/DB를 꺼내서 실제 보고서/그래프로 확장하기

CSV/SQLite 내보내기 엑셀 피벗/차트 pandas 분석 보고서 PNG/HTML

Microsoft Excel 로고
+
Python 로고
오늘은 “센서 만들기”가 아니라 “센서 데이터로 이야기 만들기(보고서)”를 합니다.
도입 이야기
전시가 끝나고 학생이 말합니다.
“저희가 만든 장치가 진짜로 의미가 있었는지 보고서로 보여주고 싶어요!”

그때 필요한 게 데이터 분석입니다.
오늘은 CSV/DB를 꺼내서 엑셀과 파이썬으로
요약 통계 + 추세 그래프 + 주간 리포트까지 확장합니다.

📊 21강 핵심 한눈에 보기

21강: 분석 & 보고서 CSV/DB → 정리 → 통계/피벗 → 그래프 → 보고서(PNG/HTML/PDF) ① 데이터 꺼내기 CSV 파일 SQLite 쿼리 기간 선택 ② 분석 AVG/MIN/MAX 시간대/요일 패턴 이상치 탐지 ③ 결과물 엑셀 피벗/차트 파이썬 그래프 PNG 리포트 자동 생성 보고서 문장 포인트 ✓ “언제(시간대) 변화가 컸는가?” ✓ “왜 그랬을까?”(환경 요인 가설) ✓ “개선 아이디어”까지 제시하면 발표 퀄리티가 급상승

1️⃣ 준비: 데이터 위치 확인(20강 기준)

20강에서 날짜별 CSV/리포트를 만들었고, 19강에서 SQLite DB를 만들었다고 가정합니다.

# CSV(날짜별)
ls -lh ~/data/archive

# 리포트(자동 생성)
ls -lh ~/data/reports

# DB(SQLite)
ls -lh ~/data/sensor.db
분석은 보통 “최근 7일” 또는 “특정 행사 기간(전시 기간)”을 잡고 시작하면 딱 좋습니다.

2️⃣ 엑셀 분석: 가장 빠른 “보고서 느낌” 만들기

(A) CSV 한 번에 합치기(추천)

날짜별 CSV가 여러 개면, 엑셀에서 열어서 합칠 수도 있지만 “파워쿼리”가 가장 편합니다.
(학교 PC 환경이 제각각이면, 아래 “파이썬으로 합친 뒤 엑셀로” 방식도 매우 안정적입니다.)

(B) 피벗테이블 추천 구조

목표피벗 설정(예시)해석 포인트
시간대별 평균 행: 시간대(0~23)
값: 온도 평균, 습도 평균
“언제 가장 더웠나/건조했나?”
요일별 패턴 행: 요일
값: 온도 평균
수업일/휴일 차이 가설
최고/최저 값: 온도 최대/최소, 습도 최대/최소 이상치(급상승) 원인 추정
엑셀 보고서 문장 템플릿(바로 복붙 가능)
“최근 7일의 측정 결과, 온도는 평균 ○○℃였으며 최고 ○○℃는 ○월 ○일 ○시경에 관측되었다. 해당 시간대는 (창문 개방/햇빛/난방) 등 환경 요인이 영향을 준 것으로 추정된다.”

3️⃣ 파이썬 분석: ‘자동 보고서’로 확장하기

파이썬의 장점은 “한 번 만들어두면 매주 자동으로 같은 형식의 보고서”를 만들 수 있다는 점입니다.
아래는 CSV든 DB든 둘 다 분석 가능한 스크립트를 제공합니다.

필수 설치

sudo apt update
sudo apt install -y python3-pip
pip3 install pandas matplotlib
그래프는 PNG로 저장해두면, 티스토리 글/발표 PPT/보고서에 그대로 넣기 편합니다.

4️⃣ (방법 1) CSV 여러 개를 합쳐서 분석

분석 스크립트 만들기

mkdir -p ~/analysis
cd ~/analysis
nano analyze_csv.py
import glob, os
import pandas as pd
import matplotlib.pyplot as plt

CSV_GLOB = "/home/pi/data/archive/sensor_*.csv"
OUT_DIR  = "/home/pi/analysis/out"
os.makedirs(OUT_DIR, exist_ok=True)

# CSV 형식: iso,ts,temp,humi (20강에서 헤더 없이 저장했음)
files = sorted(glob.glob(CSV_GLOB))
if not files:
    raise SystemExit("CSV 파일이 없습니다: " + CSV_GLOB)

dfs = []
for f in files:
    df = pd.read_csv(f, header=None, names=["iso","ts","temp","humi"])
    dfs.append(df)

data = pd.concat(dfs, ignore_index=True)

# 타입 정리
data["ts"] = pd.to_numeric(data["ts"], errors="coerce")
data["temp"] = pd.to_numeric(data["temp"], errors="coerce")
data["humi"] = pd.to_numeric(data["humi"], errors="coerce")

# 시간 처리(한국시간 기준으로 보고 싶으면 tz 조정이 필요할 수 있음)
# 여기서는 iso 문자열을 그대로 datetime으로 변환(로컬 기준으로 취급)
data["dt"] = pd.to_datetime(data["iso"], errors="coerce")

data = data.dropna(subset=["dt","temp","humi"]).sort_values("dt")

# ---- 1) 기본 통계 ----
summary = {
    "rows": len(data),
    "temp_avg": float(data["temp"].mean()),
    "temp_min": float(data["temp"].min()),
    "temp_max": float(data["temp"].max()),
    "humi_avg": float(data["humi"].mean()),
    "humi_min": float(data["humi"].min()),
    "humi_max": float(data["humi"].max()),
}

# 최고 온도 발생 시각(행)
peak_row = data.loc[data["temp"].idxmax()]

# ---- 2) 시간대별 평균(0~23) ----
data["hour"] = data["dt"].dt.hour
hourly = data.groupby("hour")[["temp","humi"]].mean().reset_index()

# ---- 3) 일별 평균(추세) ----
daily = data.set_index("dt")[["temp","humi"]].resample("D").mean().reset_index()

# ---- 4) 그래프 저장 ----
plt.figure(figsize=(10,4))
plt.plot(data["dt"], data["temp"])
plt.title("Temperature over time (raw)")
plt.xlabel("time")
plt.ylabel("temp (C)")
plt.tight_layout()
plt.savefig(os.path.join(OUT_DIR, "temp_timeseries.png"), dpi=150)
plt.close()

plt.figure(figsize=(10,4))
plt.plot(daily["dt"], daily["temp"])
plt.title("Daily mean temperature")
plt.xlabel("date")
plt.ylabel("temp (C)")
plt.tight_layout()
plt.savefig(os.path.join(OUT_DIR, "temp_daily_mean.png"), dpi=150)
plt.close()

plt.figure(figsize=(10,4))
plt.plot(hourly["hour"], hourly["temp"])
plt.title("Hourly mean temperature (0-23)")
plt.xlabel("hour")
plt.ylabel("temp (C)")
plt.tight_layout()
plt.savefig(os.path.join(OUT_DIR, "temp_hourly_mean.png"), dpi=150)
plt.close()

# ---- 5) 텍스트 보고서 저장 ----
report_path = os.path.join(OUT_DIR, "report_summary.md")
with open(report_path, "w", encoding="utf-8") as f:
    f.write("# 센서 데이터 분석 리포트(CSV)\n\n")
    f.write(f"- 총 데이터 수: {summary['rows']}\n")
    f.write(f"- 온도(℃): 평균 {summary['temp_avg']:.2f} / 최저 {summary['temp_min']:.2f} / 최고 {summary['temp_max']:.2f}\n")
    f.write(f"- 습도(%): 평균 {summary['humi_avg']:.2f} / 최저 {summary['humi_min']:.2f} / 최고 {summary['humi_max']:.2f}\n\n")
    f.write("## 최고 온도 발생\n")
    f.write(f"- 시각: {peak_row['dt']}\n")
    f.write(f"- 온도: {peak_row['temp']}℃ / 습도: {peak_row['humi']}%\n\n")
    f.write("## 생성된 그래프\n")
    f.write("- temp_timeseries.png\n- temp_daily_mean.png\n- temp_hourly_mean.png\n")

print("OK. output:", OUT_DIR)

실행

python3 analyze_csv.py
ls -lh ~/analysis/out
결과물
- 그래프 PNG 3장
- report_summary.md(요약 보고서)
이 파일들을 그대로 티스토리 글/수업자료/PPT에 활용할 수 있습니다.

5️⃣ (방법 2) SQLite DB에서 기간을 골라 분석(더 강력)

DB 분석 스크립트

nano analyze_db.py
import os, sqlite3
import pandas as pd
import matplotlib.pyplot as plt

DB_PATH = "/home/pi/data/sensor.db"
OUT_DIR = "/home/pi/analysis/out_db"
os.makedirs(OUT_DIR, exist_ok=True)

# 최근 N일만 분석(예: 7일)
DAYS = 7

con = sqlite3.connect(DB_PATH)

# 기간 필터: now - DAYS
query = f"""
SELECT ts, iso, temp, humi
FROM sensor_log
WHERE ts >= (strftime('%s','now') - {DAYS}*86400)
ORDER BY ts ASC;
"""

data = pd.read_sql_query(query, con)
con.close()

if data.empty:
    raise SystemExit("DB에 데이터가 없습니다(기간을 늘려보세요).")

data["dt"] = pd.to_datetime(data["iso"], errors="coerce")
data = data.dropna(subset=["dt","temp","humi"]).sort_values("dt")

# 요약
summary = {
    "rows": len(data),
    "temp_avg": float(data["temp"].mean()),
    "temp_min": float(data["temp"].min()),
    "temp_max": float(data["temp"].max()),
    "humi_avg": float(data["humi"].mean()),
    "humi_min": float(data["humi"].min()),
    "humi_max": float(data["humi"].max()),
}

peak_row = data.loc[data["temp"].idxmax()]

# 요일/시간대 패턴
data["weekday"] = data["dt"].dt.day_name()   # 영어 요일(원하면 한글로 매핑)
data["hour"] = data["dt"].dt.hour
hourly = data.groupby("hour")[["temp","humi"]].mean().reset_index()
wday = data.groupby("weekday")[["temp","humi"]].mean().reset_index()

# 그래프
plt.figure(figsize=(10,4))
plt.plot(data["dt"], data["temp"])
plt.title(f"Temperature over time (last {DAYS} days)")
plt.xlabel("time")
plt.ylabel("temp (C)")
plt.tight_layout()
plt.savefig(os.path.join(OUT_DIR, "temp_last_days.png"), dpi=150)
plt.close()

plt.figure(figsize=(10,4))
plt.plot(hourly["hour"], hourly["temp"])
plt.title("Hourly mean temperature")
plt.xlabel("hour")
plt.ylabel("temp (C)")
plt.tight_layout()
plt.savefig(os.path.join(OUT_DIR, "temp_hourly_mean.png"), dpi=150)
plt.close()

# 요일 그래프(카테고리)
plt.figure(figsize=(10,4))
plt.plot(wday["weekday"], wday["temp"])
plt.title("Weekday mean temperature")
plt.xlabel("weekday")
plt.ylabel("temp (C)")
plt.tight_layout()
plt.savefig(os.path.join(OUT_DIR, "temp_weekday_mean.png"), dpi=150)
plt.close()

# 보고서
report_path = os.path.join(OUT_DIR, "report_db.md")
with open(report_path, "w", encoding="utf-8") as f:
    f.write(f"# 센서 데이터 분석 리포트(DB, 최근 {DAYS}일)\\n\\n")
    f.write(f"- 총 데이터 수: {summary['rows']}\\n")
    f.write(f"- 온도(℃): 평균 {summary['temp_avg']:.2f} / 최저 {summary['temp_min']:.2f} / 최고 {summary['temp_max']:.2f}\\n")
    f.write(f"- 습도(%): 평균 {summary['humi_avg']:.2f} / 최저 {summary['humi_min']:.2f} / 최고 {summary['humi_max']:.2f}\\n\\n")
    f.write("## 최고 온도 발생\\n")
    f.write(f"- 시각: {peak_row['dt']}\\n")
    f.write(f"- 온도: {peak_row['temp']}℃ / 습도: {peak_row['humi']}%\\n\\n")
    f.write("## 해석 포인트(예시)\\n")
    f.write("- 시간대별 평균 그래프를 보고, 급상승 시간대에 주변 요인을 기록해 원인을 추정한다.\\n")
    f.write("- 요일별 평균으로 수업일/휴일 차이 또는 난방/환기 패턴을 가설로 세운다.\\n\\n")
    f.write("## 생성된 그래프\\n")
    f.write("- temp_last_days.png\\n- temp_hourly_mean.png\\n- temp_weekday_mean.png\\n")

print("OK. output:", OUT_DIR)

실행

python3 analyze_db.py
ls -lh ~/analysis/out_db
DB 분석이 좋은 이유
“최근 7일 / 최근 30일 / 특정 날짜” 같은 조건을 쿼리로 깔끔하게 자를 수 있어서, 보고서 자동화에 특히 강합니다.

6️⃣ 보고서/발표 퀄리티를 올리는 ‘해석 프레임’

질문분석보고서 문장 예시
언제 변화가 컸나? 시간대 평균 + 최고 발생 시각 “○시대에 평균 온도가 높고, 최고치는 ○시에 발생했다.”
왜 그랬을까? 환경 요인 가설(환기/햇빛/난방) “해당 시간대는 (창문 개방/햇빛) 영향 가능성이 있다.”
어떻게 개선할까? 운영 제안(알림 임계값/환기 규칙) “다음 운영에서는 ○℃ 이상일 때 환기 알림을 제공한다.”

7️⃣ 학생 미션(수준별)

  1. 기본: CSV 분석 스크립트를 실행해 그래프 PNG 3장 만들기
  2. 기본: report_summary.md에 “해석 문장 3줄” 추가하기
  3. 중급: “온도 30℃ 이상” 데이터만 따로 필터링해서 개수/비율 구하기
  4. 중급: 일별 최고 온도를 뽑아 ‘일별 최고치 그래프’ 만들기
  5. 상급: DB 분석에서 기간(DAYS)을 인자로 받아 실행되게 개선하기
상급 한 줄
“장치 제작(메이커) → 데이터 축적(IoT) → 분석(과학적 해석) → 개선(공학적 최적화)”
이 흐름이 완성되면 프로젝트가 ‘연구’가 됩니다.

8️⃣ 유의사항(현장에서 자주 터지는 것)

  • 시간(타임존): iso가 UTC처럼 보일 수 있습니다. 보고서 기준 시간을 통일하세요(로컬/UTC 중 하나).
  • 결측/이상치: 센서 오류로 튀는 값이 있을 수 있어요. 최고/최저 해석 전에 “이상치” 여부를 확인하세요.
  • 저장 주기: 5초 데이터는 그래프가 촘촘하지만 용량이 빠르게 늘어납니다(전시용은 30초~1분 추천).
  • 파일 이동: 생성된 PNG/MD는 USB로 복사해 PPT/보고서에 넣으면 끝입니다.

▶ 다음 강좌 예고(심화 예시)

22강. AI 분석(확장) – 이상치 자동 탐지/예측(간단 회귀)로 ‘미래 온도’ 예측 그래프 만들기

반응형

+ Recent posts