๋งค์ผ, ๋งค์ฃผ ๋ฐ๋ณต๋๋ ์์
์ด ์์ต๋๊น? ์๋ฅผ ๋ค์ด ๋ณด๊ณ ์ ์์ฑ. ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๊ณ , ๋ถ์ํ๊ณ , ์๊ฐํ(๊ทธ๋ํ, ์ฐจํธ ์์ฑ)ํ ํ ์์ฌ์๊ฒ ๋ณด๋
๋๋ค. ํ์ง๋ง ์ด ๋ชจ๋ ๊ฒ์ด ์๋ํ๋๋ค๋ฉด ์ด๋จ๊น์?
์ด ํํ ๋ฆฌ์ผ์์๋ ๋ณด๊ณ ๋ฅผ ์๋ํํ๋ ๋ฐ ๋์์ด ๋๋ Telegram์ฉ ๋ด์ ๋ง๋ญ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ฅ ๋ฉ์ง ์ ์ ์ ์ฒด ํ๋ก๊ทธ๋จ์ด ๋จ 50์ค์ ์ฝ๋๋ก ๊ตฌ์ฑ๋๋ค๋ ๊ฒ์
๋๋ค! ์ฒ์์ผ๋ก Telegram์ฉ ๋ด์ ์์ฑํ๋ ๊ฒฝ์ฐ ์ด ๋ด์ฉ๋ ์ฝ์ด์ผ ํฉ๋๋ค.
Skillbox๋ ๋ค์์ ๊ถ์ฅํฉ๋๋ค. ์ค๊ธฐ ์ฝ์ค
์ฒ์๋ถํฐ Python ๊ฐ๋ฐ์ .์๋ฆผ: "Habr"์ ๋ชจ๋ ๋ ์๋ฅผ ์ํ - "Habr" ํ๋ก๋ชจ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ Skillbox ๊ณผ์ ์ ๋ฑ๋กํ ๋ 10 ๋ฃจ๋ธ ํ ์ธ.
์์ํ์
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
์ฐ๋ฆฌ๋ ์ฌ์ฉํ ๊ฒ์ด๋ค
pip3 ์ค์น google-cloud-bigquery matplotlib numpy pandas python-telegram-bot
Google BigQuery API ์ฐ๊ฒฐ
์๋น์ค๋ฅผ ์ด์ฉํ๋ ค๋ฉด Google BigQuery API๋ฅผ ์ฐ๊ฒฐํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด ์ฐ๋ฆฌ๋
์ ์ดํ์์ API ๋ฐ ์๋น์ค ํ์ฑํ๋ฅผ ์ ํํ๊ณ BigQuery API๋ฅผ ์ฐพ์ผ์ธ์.
ํ์ฑํ๋ฅผ ์ ํํ์ฌ API๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค.
๊ณ์ ํค ๋ง๋ค๊ธฐ
๋ค์ ๊ฐ์
๊ทธ๋ฐ ๋ค์ - ์ ์๋น์ค ๊ณ์ ์ ์ ํํ๊ณ ์๋น์ค ๊ณ์ ์ด๋ฆ ํ๋์ ์ด๋ฆ์ ์ ๋ ฅํฉ๋๋ค.
์ญํ ๋๋กญ๋ค์ด ๋ชฉ๋ก์์ ํ๋ก์ ํธ > ์์ ์๋ฅผ ์ ํํ ๋ค์ ๋ง๋ค๊ธฐ๋ฅผ ์ ํํฉ๋๋ค.
์๋์ผ๋ก ๋ค์ด๋ก๋๋๋ ํ์ผ์ creds.json์
๋๋ค.
ํฐ๋ฏธ๋์์ creds.json ๊ฒฝ๋ก๋ฅผ ์ง์ ํ์ฌ GOOGLE_APPLICATION_CREDENTIALS๋ฅผ ์ค์ ํฉ๋๋ค.
GOOGLE_APPLICATION_CREDENTIALS='[PATH_TO_CREDS.JSON]' ๋ด๋ณด๋ด๊ธฐ
๋ชจ๋ ๊ฒ์ด ์์กฐ๋กญ๊ฒ ์งํ๋์๋ค๋ฉด ์ด์ ํ๋ก๊ทธ๋จ ์์ฑ์ ์์ํ ์ฐจ๋ก์ ๋๋ค.
์์ฉ ํ๋ก๊ทธ๋จ ๋ง๋ค๊ธฐ
ํํ ๋ฆฌ์ผ์์๋ bigquery-public-data.stackoverflow์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ , ๋ณด๊ณ ์์์๋ ์ผ์ผ ๋ฐํ ํ์๋ฅผ ์ ํํฉ๋๋ค.
๋ชจ๋ ๊ฒ์ด ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค.
ํ ์ด๋ธ ์ฟผ๋ฆฌ -> ๋ฐ์ดํฐ ์๊ฐํ -> ์๊ฐํ ์ ์ฅ -> ์ด๋ฏธ์ง ๋ณด๋ด๊ธฐ
๊ฐ ์ค๋ ๋๋ฅผ ์ ์ํ๋ ํ๋์ ํจ์๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
BigQuery์ ์ฟผ๋ฆฌ
๋จผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
google.cloud์์ BigQuery ๊ฐ์ ธ์ค๊ธฐ
๋งค๊ฐ๋ณ์๊ฐ query์ธ query_to_bigquery๋ผ๋ ํจ์๋ฅผ ๋ง๋ญ๋๋ค.
def query_to_bigquery(query):
client = bigquery.Client()
query_job = client.query(query)
result = query_job.result()
dataframe = result.to_dataframe()
return dataframe
์ด ํจ์๋ ์์ฒญ์ ๋ฐ์ดํฐ ํ๋ ์์ผ๋ก ๋ฐํํฉ๋๋ค.
๋ฐ์ดํฐ ์๊ฐํ
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด matplotlib๋ฅผ ์ ํํ์ธ์.
matplotlib.pyplot์ plt๋ก ๊ฐ์ ธ ์ค๊ธฐ
XNUMX๊ฐ์ ๋งค๊ฐ๋ณ์๊ฐ ํ์ํฉ๋๋ค. ์ฌ๊ธฐ์ x๋ x์ถ ๋ฐ์ดํฐ, x_label์ ์ถ ์ ๋ชฉ, y๋ y์ถ ๋ฐ์ดํฐ, y_label์ ์ถ ์ ๋ชฉ, title์ ์ ์ฒด ์๊ฐํ์ ์ ๋ชฉ์ ๋๋ค.
def visualize_bar_chart(x, x_label, y, y_label, title):
plt.title(title)
plt.xlabel(x_label)
plt.ylabel(y_label)
index = np.arange(len(x))
plt.xticks(index, x, fontsize=5, rotation=30)
plt.bar(index, y)
return plt
์ด๋ฏธ์ง ์ ์ฅ
์ด์ ๋ ๊ฐ์ง ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐํ๋ฅผ ๋ง๋ค๊ณ ์ ์ฅํด ๋ณด๊ฒ ์ต๋๋ค.
๋งค์ผ ๊ฒ์๋ ๊ฒ์๋ฌผ ์๋ฅผ ๋ณด๋ด๋๋ฆฝ๋๋ค. ๋จผ์ ์์ฒญ์ ์์ฑํฉ๋๋ค.
query = """
SELECT DATE(creation_date) date, COUNT(*) total_posts
FROM `bigquery-public-data.stackoverflow.post_history`
GROUP BY 1
HAVING date > DATE_SUB('2018-12-02', INTERVAL 14 DAY)
ORDER BY 1
"""
์ฟผ๋ฆฌ๋ 2๋ 2018์ XNUMX์ผ๋ถํฐ XNUMX์ฃผ ๋์ ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
2018-12-02๊ฐ bigquery-public-data.stackoverflow.post_history์ ๊ธฐ๋ก๋ ์ต์ ๋ฐ์ดํฐ์ด๊ธฐ ๋๋ฌธ์ ์ด ๋ ์ง๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ค๋ฅธ ๊ฒฝ์ฐ์๋ CURRENT_DATE()๋ฅผ ์ฌ์ฉํ์ฌ ์ต์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
query_to_bigquery ํจ์๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
๋ฐ์ดํฐํ๋ ์ = query_to_bigquery(์ฟผ๋ฆฌ)
๊ทธ๋ฐ ๋ค์ x์ถ์ ๋ ์ง ๋ฐ์ดํฐ ์ด์ ์ฌ์ฉํ๊ณ y์ถ์ total_posts ์ด์ ์ฌ์ฉํฉ๋๋ค.
x = ๋ฐ์ดํฐํ๋ ์['๋ ์ง'].tolist()
y = ๋ฐ์ดํฐํ๋ ์['total_posts'].tolist()
์๊ฐํ_bar_chart ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐํํ๊ณ ์ด๋ฏธ์ง๋ก ์ ์ฅํฉ๋๋ค.
plt = visible_bar_chart(x=x, x_label='๋ ์ง', y=y, y_label='์ด ๊ฒ์๋ฌผ', title='์ผ์ผ ๊ฒ์๋ฌผ')
plt.savefig('viz.png')
์ด ์ฝ๋๋ฅผ get_and_save_image๋ผ๋ ํจ์๋ก ๋ํํฉ๋๋ค.
def get_and_save_image():
query = """
SELECT DATE(creation_date) date, COUNT(*) total_posts
FROM `bigquery-public-data.stackoverflow.post_history`
GROUP BY 1
HAVING date > DATE_SUB('2018-12-02', INTERVAL 14 DAY)
ORDER BY 1
"""
dataframe = query_to_bigquery(query)
x = dataframe['date'].tolist()
y = dataframe['total_posts'].tolist()
plt = visualize_bar_chart(x=x, x_label='Date', y=y, y_label='Total Posts', title='Daily Posts')
plt.savefig('viz.png')
์ด๋ฏธ์ง ๋ณด๋ด๊ธฐ
์์ ์์๊ฒ ๋ณด๊ณ ์๋ฅผ ๋ณด๋ด๋ ค๋ฉด chat_id ๋งค๊ฐ๋ณ์๋ฅผ ์์์ผ ํฉ๋๋ค.
์ฐ๋ฆฌ๋ ์ฌ์ฉ
์ด์ send_image ํจ์๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. get_and_save_image ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ๊ฒ์ํ๊ณ ์ ์ฅํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ๋ชจ๋ ๊ฒ์ ์ฌ๋ฐ๋ฅธ ์ฐ๋ฝ์ฒ๋ก ๋ณด๋ ๋๋ค.
def send_image(bot, update):
get_and_save_image()
chat_id = 'CHAT_ID_RECEIVER'
bot.send_photo(chat_id=chat_id, photo=open('viz.png','rb'))
์ฃผ์ ํ๋ก๊ทธ๋จ
๋ง์ง๋ง์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํ๋ ๋ ๋ค๋ฅธ ํจ์์ธ main์ ์์ฑํฉ๋๋ค. ๋ด์ YOUR_TOKEN์ ๋ณ๊ฒฝํ๋ ๊ฒ์ ์์ง ๋ง์ธ์.
๊ธฐ์ตํ์ธ์: ์ด ํ๋ก๊ทธ๋จ์ ๊ทํ๊ฐ ์ง์ ํ ์๊ฐ์ ์๋์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ๋ณด๋ ๋๋ค. ์๋ฅผ ๋ค์ด ๋งค์ผ ์์นจ XNUMX์์ ๋ณด๊ณ ์๋ฅผ ๋ณด๋ด๋๋ฆฝ๋๋ค.
def main():
updater = Updater('YOUR_TOKEN')
updater.job_queue.run_daily(send_image, time=datetime.datetime.strptime('9:00AM', '%I:%M%p').time(), days=(0,1,2,3,4,5,6))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
๊ฒฐ๊ณผ์ ์ผ๋ก ์ฐ๋ฆฌ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
from google.cloud import bigquery
from telegram.ext import Updater
import matplotlib.pyplot as plt
import numpy as np
import datetime
def query_to_bigquery(query):
client = bigquery.Client()
query_job = client.query(query)
result = query_job.result()
dataframe = result.to_dataframe()
return dataframe
def visualize_bar_chart(x, x_label, y, y_label, title):
plt.title(title)
plt.xlabel(x_label)
plt.ylabel(y_label)
index = np.arange(len(x))
plt.xticks(index, x, fontsize=5, rotation=30)
plt.bar(index, y)
return plt
def get_and_save_image():
query = """
SELECT DATE(creation_date) date, COUNT(*) total_posts
FROM `bigquery-public-data.stackoverflow.post_history`
GROUP BY 1
HAVING date > DATE_SUB('2018-12-02', INTERVAL 14 DAY)
ORDER BY 1
"""
dataframe = query_to_bigquery(query)
x = dataframe['date'].tolist()
y = dataframe['total_posts'].tolist()
plt = visualize_bar_chart(x=x, x_label='Date', y=y, y_label='Total Posts', title='Daily Posts')
plt.savefig('viz.png')
def send_image(bot, update):
get_and_save_image()
chat_id = 'CHAT_ID_RECEIVER'
bot.send_photo(chat_id=chat_id, photo=open('viz.png', 'rb'))
def main():
updater = Updater('YOUR_TOKEN')
updater.job_queue.run_daily(send_image, time=datetime.datetime.strptime('9:00AM', '%I:%M%p').time(), days=(0,1,2,3,4,5,6))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
ํ์ผ์ ์ ์ฅํ๊ณ ์ด๋ฆ์ main.py๋ก ์ง์ ํฉ๋๋ค.
ํฐ๋ฏธ๋์ ๋ค์ ๋ช ๋ น์ ์ ๋ ฅํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ํฉ๋๋ค.
ํ์ด์ฌ3 ๋ฉ์ธ.py
๋ชจ๋ ๊ฒ์ด ์ค๋น๋์์ต๋๋ค. ์ด์ ์ฐ๋ฆฌ๋ ์ฐ๋ฆฌ์ ๊ฐ์ ์์ด ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ 50์ค์ ์ฝ๋๋ก ๊ตฌ์ฑ๋ ๋ก๋ด์ ๊ฐ๊ฒ ๋์์ต๋๋ค.
๋ด์ ํ์ธํด ๋ณผ๊น์?
์์ฑ๋ ์ฝ๋๋ ๋ค์์์ ์ป์ ์ ์์ต๋๋ค.
Skillbox๋ ๋ค์์ ๊ถ์ฅํฉ๋๋ค.
- XNUMX๋ ์ค์ต ์ฝ์ค
"์ ๋ PRO ์น ๊ฐ๋ฐ์์ ๋๋ค" .- ์จ๋ผ์ธ ์ฝ์ค
"0์ C# ๊ฐ๋ฐ์" .- ์ค๊ธฐ ์ฝ์ค
"0์์ PRO๋ก์ PHP ๊ฐ๋ฐ์" .
์ถ์ฒ : habr.com