보통 패킹된 데이터를 풀 때, index를 많이 활용한다.

가령 {'abc||def', 'abc2||def2', ....., 'abc10000||def10000'} 라는 set가 있다고 하자.[각주:1]

이 set에서 '||'를 구분자로 해서 자르고 앞 뒤를 각각 변수로 담는다고 하면,

for item in chr_set:
    first = item.split("||")[0]
    second = item.split("||")[1]

이런 형태로 구문을 쓸 수 있다.

그러나 파이썬에선 다음과 같은 형태로 언패킹을 할 수 있다.

for item in chr_set:
    first, second = item.split("||")

 

또 dict형태라고 하면,

snacks = {
    '과자': ('치토스', 120),
    '젤리': ('하리보', 70),
    '쿠키': ('칙촉', 90),
}
for kind, (name, cal) in snacks.items():
    print(kind, name, cal)

이렇게 순서쌍만 맞춰주면 바로 할당이 된다.

과자 치토스 120
젤리 하리보 70
쿠키 칙촉 90

 

특이하게도 서로간 값을 교환하는 경우에도 가능하다.

str_source = 'value'
str_target = 'value2'
print(str_source)
print(str_target)
str_source, str_target = str_target, str_source
print(str_source)
print(str_target)
value
value2
value2
value

 

  1. 뒤에 숫자는 그냥 붙여둔 것이고 사실상 그냥 랜덤한 값들이 차있다고 생각하자. [본문으로]

개발을 하다보면,

if condition == '1':
  a = 'abc'
elif condition == '2':
  a = 'bcd'
elif condition == '3':
  a = 'cde'
else:
  a = 'def'

 

이런 전개를 자주 하는 버릇이 있다.

이때 If를 먼저 검사하고 아니면 Elif, 아니면 그다음 Elif, 다 아니면 Else 형태로 실행하게 되는데

이게 반복문 안에 있을 경우 항상 체크로직을 거치게 된다.

가독성은 논의해볼 수 있으나, 퍼포먼스를 좋게 하기 위해선, 미리 값을 할당해놓고 뽑아서 쓰는 게 좋다.

 

이런 경우엔 dict에 저장할 수 있다.

dict_master = {'1' : 'abc', '2' : 'bcd', '3' : 'cde'}

a = dict_master.get(condtion, 'def') # 키값이 없으면 'def'를 할당함

 

1000만번 기준으로 해보니

else 포함 4가지인 경우

if : 0.43928680000000003
dict lookup : 0.3785323

else 포함 6가지인 경우
if : 0.6370448
dict lookup : 0.379486

else 포함 8가지인 경우

if : 0.9782373000000001
dict lookup : 0.3811875

이런 결과가 나왔다.

[하기 이미지는 퍼온 것]

Performance 차이. 여기선 7개부터 차이가 커진다.

------------------------------------------------------------------------------

C# 베이스인 Uipath에서 하려면 TryGetValue로 쓰면 된다.

TryGetValue 쓰기

(Assign에서는 ref 함수가 지원이 안 돼서 invoke method로 설정해서 해야 한다. Tryparse 같은 함수도 비슷하다.)

 

이런 식의 패턴을 예상한다.

invoke method

if string.isNullOrWhiteSpace(sTest)

  sTest = "기본값"

end if

 

 

Try~Catch를 쓸 수도 있는데 If에 비해 퍼포먼스가 항상 안 좋다는 걸 생각하면,

위의 패턴이 최선으로 보인다.

https://rito15.github.io/posts/cs-if-vs-try-performance/

 

C# - If vs try-catch 성능

Note

rito15.github.io

 

기본 하루 할당량이 10000 Quota인데 키워드 하나에 120 정도 소모된다.

search list(100) + 영상 하나 당 detail(1)*20

from googleapiclient.discovery import build
import pandas as pd
import datetime as dt


class YoutubeVideoapi:
    def __init__(self):
        self.developer_key = 'Replace Me'
        self.youtube_api_service_name = "youtube"
        self.youtube_api_version = 'v3'

    def videolist(self, keyword):
        youtube = build(self.youtube_api_service_name, self.youtube_api_version, developerKey=self.developer_key)

        search_response = youtube.search().list(
            q=keyword,
            order='viewCount',
            part='snippet',
            maxResults=20
        ).execute()
        # print(search_response)
        # 검색을 위한 videoID 추출
        video_ids = []
        for i in range(0, len(search_response['items'])):
            video_ids.append((search_response['items'][i]['id']['videoId']))

        search_date_list = []
        keyword_list = []
        channel_video_id = []
        channel_video_title = []
        channel_rating_view = []
        channel_rating_comments = []
        channel_rating_good = []
        channel_published_date = []
        data_dicts = {'검색날짜': [], '키워드': [], 'ID': [], '제목': [], '조회수': [], '댓글수': [], '좋아요수': [],
                 '게시일': []}
        # 영상이름, 조회수 , 좋아요수 등 정보 등 추출
        for k in range(0, len(search_response['items'])):
            video_ids_lists = youtube.videos().list(
                part='snippet, statistics',
                id=video_ids[k],
            ).execute()
            # print(video_ids_lists)

            # str_title = video_ids_lists['items'][0]['snippet'].get('channelTitle')

            str_video_id = video_ids_lists['items'][0]['id']
            str_video_title = video_ids_lists['items'][0]['snippet'].get('title')
            str_view_count = video_ids_lists['items'][0]['statistics'].get('viewCount')
            if str_view_count is None:
                str_view_count = "0"
            str_comment_count = video_ids_lists['items'][0]['statistics'].get('commentCount')
            if str_comment_count is None:
                str_comment_count = "0"
            str_like_count = video_ids_lists['items'][0]['statistics'].get('likeCount')
            if str_like_count is None:
                str_like_count = "0"
            str_published_date = str(video_ids_lists['items'][0]['snippet'].get('publishedAt'))

            # 검색날짜 입력
            search_date_list.append(str(dt.date.today()))
            # 키워드 입력
            keyword_list.append(keyword)
            # 비디오 ID 입력
            channel_video_id.append(str_video_id)
            # 비디오 제목 입력
            channel_video_title.append(str_video_title)
            # 조회수 입력
            channel_rating_view.append(str_view_count)
            # 댓글수 입력
            channel_rating_comments.append(str_comment_count)
            # 좋아요 입력
            channel_rating_good.append(str_like_count)
            # 게시일 입력
            channel_published_date.append(str_published_date)

        data_dicts['검색날짜'] = search_date_list
        data_dicts['키워드'] = keyword_list
        data_dicts['ID'] = channel_video_id
        data_dicts['제목'] = channel_video_title
        data_dicts['조회수'] = channel_rating_view
        data_dicts['댓글수'] = channel_rating_comments
        data_dicts['좋아요수'] = channel_rating_good
        data_dicts['게시일'] = channel_published_date
        print(data_dicts)
        return data_dicts


def main(keyword):
    search_dict = YoutubeVideoapi().videolist(keyword)  # 아이디, 제목, 조회수, 댓글수, 좋아요수 추출
    df = pd.DataFrame(search_dict)  # 데이터프레임화
    str_json = df.to_json(orient='records', force_ascii=False) #DataTable로 바꾸려고 옵션 records로 JSON 변환
    print(str_json)
    return str_json

 

 

 

간단한 필터링은 방식이 굉장히 많다.

.loc부터 해서 df[조건] 같은 직관적인 방식 등등

필요한 사항은 "앞 2문자를 어떤 string이 포함하는 것"만 필터링

df_edit = df[df.apply(lambda r: str(r.col0)[:2] in str_target, axis=1)]

(역시 apply가 만능인가...)

다른 방식으로 해보려 했지만 series의 row를 str로 인식하지 못 해 실패

str로 형변환은 필요한 경우만 해도 된다.

 

df['test'] = np.where(조건, 참일 때 값, 거짓일 때 값) 형태로 쓰게 되는데

각 line별로 function을 태우고 그 값을 넣고 싶을 때가 생긴다.

그 경우 lambda를 활용할 수 있다.

df["test"] = df.apply(lambda r: func(r.col0, r.col1), axis=1)

여기서 axis는 컬럼 기준으로 작업할지, 행 기준으로 작업할지에 대한 매개변수다.

axis=1일 경우 컬럼 기준, 0인 경우 행 기준으로 작업한다.

 

함수의 return이 여러 개인 경우엔,

df["test1"], df["test2"], df["test3"] = zip(*df.apply(lambda r: func(r.col0, r.col1), axis=1))

들어갈 각 열들을 나열하고, zip으로 묶어주면 return의 순서대로 값이 들어간다.

python에서는 굉장히 간단한 형태로 string의 empty 여부를 확인한다.

string 자체를 bool이 들어갈 자리에 두면 값이 있는 경우 True, 빈 경우 False를 반환한다.

(strip()이랑 같이 쓰면 IsNullOrWhitespace 처럼 쓸 수 있다.)

여기에 and나 or 같은 논리연산자가 들어가면

가장 마지막에 검사된 값을 반환한다.

예를 들어,

True or 'test'는 True를 반환한다. (True만 검사하면 무조건 True)

True and 'test'는 'test'를 반환한다. ('test'까지 검사해야 True, False를 알 수 있기 때문)

False and 'test'는 False를 반환한다.(False만 검사하면 무조건 False)

 

이런 고로 논리연산자를 쓸 때 괄호를 쳐주지 않으면 제대로 인식이 되지 않는 케이스가 생긴다.

가령 df[df["test"] == test and df["test1"] == "test"] 같은 형태는

the truth value of a series is ambiguous. use a.empty a.bool() a.item() a.any() or a.all()

같은 에러가 난다.

df[(df["test"] == test) and (df["test1"] == "test")]

괄호로 묶어주면 식이 유효한 경우 에러가 사라진다.

 

numpy의 경우 and, or 대신 &, | 를 사용해야 되는 케이스가 많다.

파이썬은 객체 지향 함수이기 때문에 모듈을 import하고 그 객체로부터 메서드(함수)를 뽑아 쓴다.

먼저 메서드가 정의된 파일(모듈)이 abc.py라 가정하고 다음과 같은 내용이 있다고 하자.

def add(a, b):
    return a+b

def minus(a,b):
    return a-b

import abc를 하면 abc 객체를 생성할 수 있다.

그 객체에서 메서드를 뽑아서 사용하면 된다.

a = abc.add(3, 5)

b = abc.minus(3, 5)

 

다른 패키지들도 마찬가지다.

가령 DB에 연결하게 해주는 pymysql 패키지는 import된 후

conn=pymysql.connect(
  host=connHeader['host'],
  port=connHeader['port'],
  user=connHeader['user'],
  password=connHeader['password'],
  db=connHeader['db'],
  charset=connHeader['charset']
)

이런 형태로 연결 객체를 만들어 준다.

+ Recent posts