루나의 TIL 기술 블로그

pandas 데이터프레임

|

목적

판다스는 데이터 조작과 분석을 위한 파이썬 소프트웨어 라이브러리이다. 패널데이터에서 유래했다. 같은 데이터를 여러 객체로 만들어서 수정, 삭제 등 여러 데이터 조작을 해볼 수 있고 메모리에만 올라가서 속도가 빠르다.

데이터프레임 예시

name math english
0 1 2
이서연 김민준 박지우
70 80 90
80 80 30

열 1개 추출하기

#시리즈 형태
df['english']

결과값 :

#데이터프레임 형태
df[['english']]

결과값 :

행 1개 추출하기

loc 함수를 이용해서는 조건 인덱싱이 가능합니다. index와 column 이름 대신 인덱싱을 원하는 조건을 loc 함수 내에 적용해주면 된다. 예를 들어, 수학 점수가 90점 이상인 학생들의 목록을 찾고 싶을 때는 다음과 같이 해주면 된다.

df.loc[]
df.loc[df['math'] >= 80]

참조: https://jimmy-ai.tistory.com/226

데이터프레임 비교시 and, or 사용

참조: https://mindscale.kr/course/pandas-basic/bool-selection/

판다스 기초 위키독스

참조: https://wikidocs.net/122729

각 열마다 함수 태워서 새 행으로 추가하기

for user in user_df['user_seq']:
    user_df['notification_yn'] = weekend_night_off(user)

데이터프레임 리스트로 변환하기

device_token_list = user_df['device_token'].tolist()
# None 제거
device_token_list = [x for x in device_token_list if x is not None]
# 중복제거
device_token_list = list(dict.fromkeys(device_token_list))

cheat sheet

https://pandas.pydata.org/Pandas_Cheat_Sheet.pdf

cheat sheet

나의 사용예시 0

sql문을 데이터프레임으로 읽어내는 코드이고 각각 데이터프레임, 리스트를 추출한다.

sql = 'select board_seq, user_seq from board'

board_df = pd.read_sql(sql, conn)
board_list = board_df.to_dict(orient='records') #1번
board_list = board_df['board_seq'].tolist() #2번

나의 사용예시 1

두 개의 데이터프레임을 합쳐서 목표주가가 달성되었는지 비교하고 달성된 게시글 번호에 해당하는 데이터를 조작하고 슬랙 메시지를 보내는 코드이다.

import pymysql
import pandas as pd
from config.config_db  import DataBaseConfig as con_DB
from configuration     import server_name, send_slack

def target_price_check(conn, conn_finance):
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = f"""
            select a.board_seq, a.symbol, a.target_price
              from db_community.tb_board_best a inner join db_community.tb_board b
                                                        on a.board_seq = b.board_seq
                                                       and b.del_yn = 0
             where a.is_accuracy = 0 
               and a.target_price is not null and a.target_price != 0 
          """
    data_df = pd.read_sql(sql, conn)
    args_list = data_df.to_dict(orient='records')
    symbol_list = [ args['symbol'] for args in args_list ]
    # print('symbol_list', symbol_list)

    cursor = conn_finance.cursor(pymysql.cursors.DictCursor)
    finance_sql = f"""
            select symbol, adj_low_prc, adj_close_prc, adj_high_prc
              from dg_db_refine_daily.tb_price_tango_data 
             where trd_dt = date_format(DATE_ADD(now(), INTERVAL -1 DAY), '%Y%m%d')
               and symbol in ({str(symbol_list).strip('[]')})			
        """
    finance_df = pd.read_sql(finance_sql, conn_finance)
    # print('finance_df', finance_df)

    df = pd.merge(data_df, finance_df, how='left')
    # print('df', df)

    true_df = df.loc[(df['adj_low_prc'] < df['target_price']) & (df['target_price'] < df['adj_high_prc'])]
    # print('true_df', true_df)

    if true_df.empty:
        send_slack(f'목표주가 달성 게시글 없음', f' tangopick_{server_name}_accuracy')
    else:
        board_list = true_df['board_seq'].tolist()
        # print('board_list', board_list)

        cursor = conn.cursor(pymysql.cursors.DictCursor)
        update_sql = f"""
            update db_community.tb_board_best
               set is_accuracy = 1, accuracy_dt = now()
             where board_seq in ({str(board_list).strip('[]')})
        """
        cursor.execute(update_sql)
        conn.commit()
        # board_list = list(map(int, board_list))
        for board_seq in board_list:
            row = df.loc[df['board_seq'] == board_seq]
            send_slack(f' 목표달성글 : https://{""if server_name == "prod" else "dev."}tangopick.co.kr/community/1/board/{board_seq} \n'
                       f' {row} ',
                       f' tangopick_{server_name}_accuracy')

    return True

if __name__ == '__main__':
    conn = pymysql.connect(**con_DB.tangopick_db_config(read=False)[0])
    conn_finance = pymysql.connect(**con_DB.finance_db_config(read=False))
    target_price_check(conn, conn_finance)
    conn.close()
    conn_finance.close()

나의 사용예시 2

구독자에 따라 유저의 등급이 변하는 코드를 5분마다 한 번씩 돌려서 등급이 변한 유저가 있으면 해당 내용을 슬랙으로 보내는 코드이다.

def save_insert_query(df, table_nm, db_name):
    df = df.where(pd.notnull(df), None)
    rows = 0

    before_sql = f"""
            select user_seq, nickname, sub_cnt, grade
              from db_user.tb_user_grade
             where grade != 'influencer'
               and nickname != '탱고픽'
        """
    df_before = pd.read_sql(before_sql, conn_tangopick)

    columns = list(df.columns)
    header_sql = f"""insert into {db_name}.{table_nm}
                     ({str(columns).strip('[]').replace("'", '`')}) 
                     values 
                   """
    body_sql = str(['%s' for i in columns]).replace("[", "(").replace("]", ")").replace("'", "")
    key_placeholders = ', '.join(['`{0}`=VALUES(`{0}`)'.format(k) for k in columns])
    footer_sql = f" on duplicate key update {key_placeholders}"

    total_query = header_sql + body_sql + footer_sql

    cursor = conn_tangopick.cursor()
    affected_rows = cursor.executemany(total_query, df.values.tolist())
    conn_tangopick.commit()

    compare_result(df_before)

    return affected_rows


def compare_result(df_before):
    after_sql = f"""
            select user_seq, nickname, sub_cnt, grade
              from db_user.tb_user_grade
             where grade != 'influencer'
               and nickname != '탱고픽'
        """
    df_after = pd.read_sql(after_sql, conn_tangopick)

    print('df_before[grade]', df_before['grade'])
    print('df_before[grade] == df_after[grade]', df_before['grade'] == df_after['grade'])
    print('df_before[(df_before[grade] == df_after[grade]) == False', df_before[(df_before['grade'] == df_after['grade']) == False])

    before = df_before[(df_before['grade'] == df_after['grade']) == False]
    after = df_after[(df_before['grade'] == df_after['grade']) == False]
    num = len(df_after[(df_before['grade'] == df_after['grade']) == False])

    if ((datetime.now().hour == 10 and datetime.now().minute == 00) or (datetime.now().hour == 11 and datetime.now().minute == 00) or
        (datetime.now().hour == 13 and datetime.now().minute == 00) or (datetime.now().hour == 14 and datetime.now().minute == 00) or
        (datetime.now().hour == 15 and datetime.now().minute == 00) or (datetime.now().hour == 16 and datetime.now().minute == 17) or
        (datetime.now().hour == 17 and datetime.now().minute == 00) or (datetime.now().hour == 18 and datetime.now().minute == 00)):
        if after.empty:
            send_slack(f'유저등급조정없음', f' tangopick_{server_name}_grade')
        else:
            send_slack(f' 유저등급조정 : {num}명 조정, \n'
                       f' 조정전: {before[["user_seq", "nickname", "grade"]]}, \n'
                       f' 조정후: {after[["user_seq", "nickname", "grade"]]}',
                       f' tangopick_{server_name}_grade')
    return True