2023년 12월 26일 화요일

푸코로 본 ChatGPT와 Bard

 

푸코의 말과 사물에서 3재현하기” 4이중화된 재현부분만 발췌해서 ChatGPT 4Bard가 읽게 하고 10줄로 요약해달라고 했습니다. 발췌한 부분이 이 블로그에 따로 첨부했습니다. 6페이지, 120 줄 정도의 분량입니다. 아래가 chatGPT이고


다음이 Bard입니다.

한글로 글을 읽는데 둘다 문제는 없었는데 chatGPT는 파일을 통째로 보낼 수 있는 반면에 Bard는 파일을 화면에서 바로 보낼 수 없고 copy&paste로 해야 합니다. BardGoogle drive 를 이용할 수 있다고 하지만 해보면 다른 파일을 읽는 오류가 있는 것 같습니다.  둘 다 한글 출력에도 문제가 없었습니다.

그런데 Bard는 본문에 포함되어 있는 않는 내용을 보여주는 한계가 있습니다.  위에 이모티콘으로 표시된 3, 6, 9번이 이상합니다.  3번은 과다하게 해석했고 6번은 엉뚱한 이야기이고 9번은 푸코와 소쉬르를 구분하지 못하는 것 같습니다.

반면에 ChatGPT 요약은 무난합니다. 특히 첨부한 본문에 없는 유사성이 언급되고 있는데 푸코의 같은 책에서 유사성언급은 바로 이어지는 5절에 나옵니다.  마지막 문장도 본문에는 직접적으로 나오지 않지만 푸코의 의도를 잘 파악한 것 같습니다.

 

결론적으로 간단한 테스트였지만 푸코의 책을 읽고 정리하는 능력은 chatGPT가 좋아 보입니다.

2023년 4월 15일 토요일

Julia가 파이썬과 다른 점

 프로그램언어 julia를 익히면서 이와 비슷한 언어 파이썬과 두드러진 차이가 나는 부분을 정리해봅니다.

1) 실행 속도


C를 비롯하여 많이 사용하는 다른 프로그램 언어와 비교한 벤치마크 결과입니다. 파이썬보다 빠를 뿐만 아니라 C 언어에 육박할 정도입니다. 이런 속도는 julia가 한 method에서는 한 type만 출력한다는 type stability를 근거로 design된 덕분입니다.


출처 : https://julialang.org/benchmarks/

 

2) Array index

Array index은 파이썬과 달리 0부터 시작하지 않고 1부터 시작합니다. 그리고 slice의 두번째 element도 포함합니다.


파이썬이었으면  a[1:3][2,3]라고 표시했을 겁니다.

range도 같습니다. 두번째 인수가 stop를 뜻하는데 파이썬은 포함되지 않지만 julia는 포함합니다.


파이썬이라면 1,2,3만 나옵니다.

 

3) String 연결

다른 언어에서는 문자열 연결에 “+”를 사용하는 반면에 julia는 특이하게 “*”를 사용합니다.  일반적으로 “+”는 교환법칙이 적용되는 반면에 “*” 는 교환법칙이 적용되지 않으니 문자열 연결에는 이 연산자에 더 맞다고 보는 것 같습니다.

 


 

4) 함수 인수 변경 가능

파이썬은 함수 인수를  내부에서 변경해도 함수 밖에서 변경된 값을 볼 수 있습니다. 함수명에 느낌표가 있어야 하고 인수는 array와 같은 mutable 변수이어야 합니다.



5) Struct

파이썬과 달리 composite type, struct를 지원합니다. 대신 class, , object oriented 프로그램은 지원하지 않습니다.



2023년 3월 19일 일요일

한국투자증권 API

 

한국투자증권은 주식 시세 조회 및 주문까지 지원하는 오픈API를 제공합니다. 타사처럼 원도우에 제한된 환경도 아니고 http 프로토콜로 접속하고 정보를 얻기 때문에 프로그램 언어 및 OS 환경의 제약을 받지 않습니다. 

여기API 문서와 샘플 코드도 있지만 주식 전종목에 대한 하루치 현재가와 거래량을 가져 오는 파이썬 코드를 작성합니다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import requests
import json
import csv
import time
import datetime
import sys

url_token = "https://openapi.koreainvestment.com:9443/oauth2/tokenP"
data = {"grant_type": "client_credentials",
        "appsecret": "6S..yQ=",
        "appkey": "PSBv..5KCmKj"}
headers = {"Content-Type": "application/json; charset=UTF-8",}
res = requests.post(url_token, data=json.dumps(data))
rescode = res.status_code
if rescode == 200:
    json_data = json.loads(res.text)
else:
    print("error code " + str(rescode) + " | " + res.text)
    sys.exit("Getting token fails") 

url = "https://openapi.koreainvestment.com:9443/uapi/domestic-stock/v1/quotations/inquire-ccnl"
headers = {'authorization' : 'Bearer ...',
           'appkey' : 'PSBvsN..CmKj',
           'appsecret' : '6S24....zGyQ=',
           'tr_id' : 'FHKST01010100'}
headers['authorization'] = 'Bearer ' + json_data['access_token']

params = {'FID_COND_MRKT_DIV_CODE' : 'J',
         'FID_INPUT_ISCD' : '950160'}

today = datetime.datetime.today()
today_formatted = today.strftime('%Y%m%d') 
with open('data_5732_20230215.csv', 'rt', encoding='euc-kr') as csvfile:
    rows = csv.reader(csvfile, delimiter=',')
    next(rows)  # skip header
    for r in rows:
        print(r[0], r[1])
        params['FID_INPUT_ISCD'] = r[0]
        time.sleep(0.3)
        res = requests.get(url, headers=headers, params=params)
        rescode = res.status_code
        if rescode == 200:
            json_data = json.loads(res.text)
        else:
            print("error code " + str(rescode) + " | " + res.text)
            break
        print(str(today_formatted), json_data['output']['stck_prpr'], json_data['output']['acml_vol'])

 

 

8: API를 웹페이지에서 신청하면 appsecretappkey를 바로 구하지만 API 실행에는 OAuth도 추가로 필요합니다. API에 따라 웹소켓과 REST 방식으로 나뉘는데 국내 주식 시세는 REST 방식이고 OAuth 인증에 접근토큰이 필요합니다. 법인이 아닌 개인을 위한 접근토큰 유효기간은 만 24시간입니다. url_token는 이 접근토큰을 위한 URL입니다.

9,10,11: 접근토근을 얻기 위한 grant_typeclient_credentials이고 appsecretappkey는 웹페이지에서 신청 즉시 구합니다.

12: HTTP POST를 위한 header입니다.

13 : 파이썬의 requests package를 이용해서 POST합니다.  post data인수에 위에서 정의한 datajson.dumps()를 통해 올립니다.

15,16 : POST가 성공적이면 결과를 json 형식으로 바꿉니다.

21 : 주식 현재가를 얻기 위한 URL입니다.

25 : 주식현재가 시세 https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations 를 위한 tr_id입니다.

26: authorization를 앞에서 얻은 접근토큰으로 변경합니다.

28, 29: 주식현재가 시세 https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations 에 나온 대로 query parameter를 설정합니다.

31, 32: 오늘 날짜를 2023-03-19 형태로 변환합니다.

33: 파일 data_5732_20230215.csv는 상장된 주식의 종목코드인데 krx 홈피  http://data.krx.co.kr/contents/MDC/MDI/mdiLoader/index.cmd?menuId=MDC0201010105 에서 구합니다.

37: 첫번째 열이 종목코드, 두번째가 종목명입니다.

38: FID_INPUT_ISCD6자리 종목코드로 지정합니다.

40: requests.get  API를 호출합니다. 인수로 headersparams를 사용합니다.

43: json 형식으로 변환한 json_data를 구합니다.

47: 현재가는 json_data[‘output’][‘stck_prpr’]이고 거래량은 json_data[‘output’][‘acml_vol’]입니다.

 

2022년 10월 18일 화요일

국회 상임위 회의록 구하기

 

국회의원 상임위 활동 데이터 설명은 아래 link에 있습니다. .

 https://open.assembly.go.kr/portal/data/service/selectAPIServicePage.do/OVW2NU000937WK15521#none

 



요청 주소

https://open.assembly.go.kr/portal/openapi/nuvypcdgahexhvrjt 에 대수, 회기, 차수, 위원회 등을 붙여서 호출하면 상임위 회의록 정보에 얻을 수 있습니다.  결과는 XML 형태이고 회의록 정보는 BILL_URL 태그에 있습니다.

 

<BILL_URL>http://likms.assembly.go.kr/record/popup_list.do?classCode=1&daeNum=20&sesNum=378&degreeNum=1&commCode=ZA&conferNum=049999</BILL_URL>

 

BILL_URL를 접속해보면 개략적인 회의 순서만 볼 수 있고


순서를 클릭해야 상임위 회의록을 다운로드 받을 수 있습니다. 한두개의 회의록은 웹브라우저 클릭으로 구하는데 어려움이 없지만 천개 넘는 모든 상임위 회의록을 구하려면 자동화 방법이 필요합니다.

위 페이지의 소스를 보면 다운로드는  fn_fileDown() 를 호출해서 이루어집니다.

<ul>

           <li><a href="#none" onclick="javascript:fn_fileDown('049996','0000093499','P');" title="개의">

 

fn_fileDown()는 이 파일에 구현되어 있습니다.

https://likms.assembly.go.kr/record/res/js/mhs/mhs40/mhs40.js


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//보존부록 파일 다운로드
function fn_fileDown(conferNum,fileId){
	//
	try{
		//
		var $form    = "";
//	    	alert(conferNum+"/"+fileId);
        $form = $("<form></form>");
        $form.attr("action", "mhs-10-040-0040.do");
        $form.attr("target", "I_TARGET");
        $form.attr("method", "post");
        
        $form.append("<input name='conferNum' type='hidden' value='"+conferNum+"'>");
        $form.append("<input name='fileId' type='hidden' value='"+fileId+"'>");
        $form.append("<input name='deviceGubun' type='hidden' value='P'>");
        //
        $form.appendTo('body');
        //
        $form.submit();
		//
	}catch(e){
        alert("fn_fileDown" + "(" +e.name + ") : " + e.message);
    }
	//
}

conferNum, fileID, deviceGubun를 넣어 POST 방식으로 호출하면 회의록 PDF파일을 구할 수 있습니다.

 BILL_URL 국회정보 일괄 다운로드 페이지에서도 일괄 구할 수 있습니다. 

https://open.assembly.go.kr/portal/infs/list/infsListDownPage.do



결과물이 엑셀 파일이고 E열이 회의종류, FBILL_URL입니다.


이 엑셀 파일을 읽어 모든 상임위의 BILL_URL를 접근하고 beautifulsoup를 이용하여 conferNum 등을 추출하고 이를 POST data에 넣어 pdf 파일을 다운로드합니다.  Postresponse content-disposition에 들어 있는 파일 이름도 추출해서 이 이름 대로 pdf 파일을 저장합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import sys
import openpyxl
from urllib.request import urlopen
import urllib
from bs4 import BeautifulSoup
import re
import requests


excel_file = openpyxl.load_workbook("데이터_국회의원 상임위 활동.xlsx")
sheet = excel_file.active
kinds = sheet['E'] # 회의종류
contents = sheet['F'] # 안건보기
for i in range(1, len(contents)):
    if kinds[i].value == "국회본회의":
        continue
    url = contents[i].value
    print(url)
    resp = urlopen(url)
    htm = str(resp.read().decode('euc-kr'))
    history = []
    bs = BeautifulSoup(htm, "html.parser")
    for x in bs.findAll("a", onclick=True):
        if re.match('javascript:fn_fileDown', x['onclick']):
            args = x['onclick'].split()
            for a in args:
                nums = a.replace("javascript:fn_fileDown(", "").replace(");", "").split(",")
                if not nums in history:
                    history.append(nums)
                    payload = {"conferNum": nums[0].replace("'", ""),
                               "fileId" : nums[1].replace("'", ""),
                               "deviceGubun" : nums[2].replace("'", "")}
                    r = requests.post("http://likms.assembly.go.kr/record/mhs-10-040-0040.do", data=payload)

                    if (r.status_code == 200):
                        d = r.headers['content-disposition']
                        flist = re.findall("filename=(.+)", d)
                        fname = flist[0]
                        # replace %xx escapes with their single-character equivalent
                        fname_2 = urllib.parse.unquote(fname, encoding='utf-8').replace(";", "")
                        print(fname_2)
                        with open(fname_2,   "wb") as f:
                            f.write(r.content)
    

2022년 9월 26일 월요일

열린국회정보

 

열린국회정보에는 150여개 국회 운영 정보가 있지만 이 중에 본회의 회의록을 받아 보려고 합니다.


본회의 회의록 OpenAPI 상세설명에 나온 대로 아래 날짜(2022년 9월 21일)이 포함된 URL을 접속하면

https://open.assembly.go.kr/portal/openapi/nzbyfwhwaoanttzje?DAE_NUM=21&CONf_DATE=2022-09-21

아래 그림에 나오는 XML 파일을 얻을 수 있습니다. 이 중에 회의 제목이 있는 <TITLE>과 회의록 PDF 위치를 알려주는 <PDF_LNK_URL>만 필요합니다.



이 파이썬 프로그램은 21대 국회 첫 본회의부터 20229 21일까지 회의록을 찾아 회의 제목 이름으로 pdf 파일을 저장합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import urllib.request
import xml.etree.ElementTree as elemTree

months = []
for m in range(12):
    num = str(m+1)
    months.append(num.zfill(2))

days = []
for d in range(31):
    num = str(d+1)
    days.append(num.zfill(2))
years = ["2020", "2021", "2022"]

base_url = "https://open.assembly.go.kr/portal/openapi/nzbyfwhwaoanttzje?DAE_NUM=21&CONF_DATE="

for y in years:
    for m in months:
        print(y + " : " + m)
        for d in days:
            if y == "2020" and m in ["01","02", "03", "04", "05"]:
                continue
            url = base_url + y + "-" + m + "-" + d             
            resp = urllib.request.urlopen(url)
            xml = resp.read().decode('utf-8')
            #print(xml)

            titles = []
            tree = elemTree.fromstring(xml)
            for t in tree.iter('TITLE'):
                if not t.text in titles:
                    titles.append(t.text)
                    
            links = []
            for p in tree.iter('PDF_LINK_URL'):
                if not p.text in links:
                    links.append(p.text)

            if len(titles) != len(links):
                print(url + ": not equal")
                break
            for ind in range(len(links)):
                print(links[ind])
                pdf_url = links[ind]
                urllib.request.urlretrieve(pdf_url, titles[ind]+".pdf")

 

회의록에서 관심이 있는 부분은 발언자입니다. 회의록에서 발언자는 모두 “O”로 시작하다는 점에 착안에서 발언자만 뽑아 볼 수 있습니다.


pdfminer를 이용해서 pdf 파일을 text로 바꾸고 “O”의원이 포함된 줄만 추출해서 출현 빈도수를 계산한 파이썬 프로그램입니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#-*- coding: utf-8 -*-
import re
from  pdfminer.high_level import extract_text
import glob

for file in glob.glob("*.pdf"):
    text = extract_text(pdf_file=file)
    #print(text)
    lines = text.split('\n')
    pattern1 ="^◯"
    pattern2 = "의원"
    speakers = {}
    for line in lines:
        if re.search(pattern1, line) and re.search(pattern2, line):
            speaker = line[1:].replace(" ", "")
            if not speaker in speakers:
                speakers[speaker] = 1
            else:
                speakers[speaker] += 1;
    print(file)
    for key, val in speakers.items():
        print(key, val)
    print("")    

2022년 3월 10일 목요일

스포티파이로 본 세계 음악

 

작년에 한국에서 서비스를 시작한 음원 스트리밍 서비스, 스포티파이는 한국에서는 크게 인기를 끌지는 못하고 있지만 데이터 관점에서는 주목한 만한 곳입니다.  인기 차트 뿐만 아니라 아티스트에게도 청취자의 정보를 제공하는 점이 특이합니다.

전세계 70개 국가의 인기 차트를 한꺼번에 볼 수 있는 데이터가 있어 특이점이 있는지 살펴 보려고 합니다.

https://www.kaggle.com/dhruvildave/spotify-charts에는 70국에 대한 차트가 있습니다. 3GB 정도의 파일 하나로 되어 있는데 아래 그림과 같은 내용을 포함하고 있습니다.   노래 제목, 순위, 날짜, 가수, URL, 국가, 차트 종류, 스트리밍 횟수 순입니다. 201711일부터 2021731일까지 기록입니다.


순위에 상관 없이 여러 나라에서 가장 많이 등장하는 노래를 찾아 보았는데 아래와 같은 순서입니다.

“Shape of You”가 가장 많이 스트리밍되었다는 뜻은 아니지만 전세계적으로 그리고 오랫동안 차트에 있었다고 봐야 합니다.

 

그리고 이 데이터에서 순위에 상관 없이 여러 나라에서 가장 많이 등장한 가수는 Ed Sheeran입니다.



BTS9번째에서 볼 수 있습니다.

 

이 데이터 외에 스포티파이는 API 형태로 앨범, 트랙, 사용자, 등의 정보를 제공합니다.

https://developer.spotify.com/documentation/web-api/reference/#/

트랙(노래) 경우 14개의 정보를 얻을 수 있습니다. 이 중에 차이점이 있어 보이는 것으로만 뽑아 나라별 분포도를 boxplot로 그립니다.

 

Acousticness

Danceability

Energy


Instrumentalness

Liveness

Speechiness

Valence


 Speechiness와 Instrumenatalness 등에서 약간 차이를 보이지만 전체적으로는 대동소이합니다.  가사를 제외한  음악 특징은 국가별로 차이가 없음을 보여 줍니다.

2022년 1월 30일 일요일

전세계 불평등한 음식 생산과 소비를 보여 주는 그래프

 

Sankey 다이아그램 중에 가장 유명한 것 중에 하나는 나폴레옹 러시아 원정도입니다.


 

선의 굵기가 병력의 수를 가리킵니다. 프랑스에서는 40만이상이 출발했는데 모스크바에 도달할 때는 이미 1/4로 줄었고 프랑스로 온전히 퇴각한 이들은 1/40 밖에 되지 않습니다.  유럽 역사에서 중요한 나폴레옹의 러시아 원정 실패를 병력 수로 신랄하게 보여 줍니다.

 

나폴레옹의 러시아 원정 만큼 의미 있는 Sankey 다이아그램이 있어 공유합니다. 아래 그림은 현재 전세계의 불평등을 보여 줍니다.  Informationis beautiful 저자와 독립언론사, New Internaltionalist가 합쳐서 만든 세계의 음식은 어디로 가느냐? 입니다.그림에 붙어 있는 영문은  한글로 수정했습니다



음식을 (붉은고기), (,오리,물고기,계란), (우유,우유가공품), (,견과류), (,,옥수수), (과일,채소) 그리고 (감자,카사바,얌과 같은 탄수화물 채소) 종류로 나누고 대륙별로 각 음식 종류의 소비량을 보여 줍니다.  그림에 나타나는 두드러진 특징은

-       


>  대륙별 소비량 차이가 존재합니다소비량이 높은 북미는 매년 인당 1203KG를 소비하는 반면에 가장 낮은 인도는 656KG으로 2:1입니다.


> 농장에서 키우는 가축은 옥수수와 같은 곡류를 크게 소비하는데 그 양이 유럽과 러시아에서 소비하는 전체 음식량과 같습니다. 이렇게 키운 붉은 고기가 인도나 아프리카로 가는 양은 미미합니다.
   
> 북미와 유럽은 음식을 다양하게 섭취하는 반면에 사하라이남 아프리카는 음식 부족함으로 인해 얌, 카사바 등이 주식입니다.

> 영양가 높은 유제품은 유럽과 러시아, 북미, 남미에서 대부분 소비됩니다.
       
> 바이오 연료로 사용되는 곡류도 인도에서 소비하는 곡류 만큼 됩니다.  


저개발국가일수록 불평등이 심하니 아프리카와 인도의 저층민은 평균보다 훨씬 적은 음식을 섭취할 가능성이 있습니다.