어? 내림차순? 그럼 정렬 알고리즘 수정해야 하나요??? 아니 그럴 필요는 없으시고요 고객님. 이거 진짜 핵 심플한 문제임.
import sys
N = sys.stdin.readline().rstrip()
x = []
for i in N:
x.append(i)
x.sort(reverse=True)
x = ''.join(x)
print(x)
파이썬은 문자도 시퀀스형 데이터이기 때문에 반복문을 돌릴 수 있다. 그래서 문자로 들어온 숫자를 for문 돌려서 하나씩 배열에 넣고(별도로 나눌 필요도 없고 그냥 for문 돌리면 하나씩 들어간다) sort를 하면 된다. 뭘 주고? reverse=True를 주고. 다음은 그걸 내림차순으로 정렬한 다음 join으로 묶어서 출력하면 끝이다.
여기서 얘기하는 산술평균이 우리가 일반적으로 얘기하는 평균이다. 평균은 산술/기하/조화평균이 있는데 이건 나중에 얘기하도록 하자... 아무튼, 그래서 4가지 요소를 어떻게 구하느냐... 평균은 일반적인 그 평균이 '맞다'. 중앙값은 말 그대로 정렬된 배열의 가운데에 있는 값이고, 최빈값은 배열에서 제일 많은 값(단, 여러개일 경우 두번째로 작은거), 범위는 최대-최소 하면 된다.
근데 여기서 최빈값이 끝판왕임.
import sys
from collections import Counter
N = int(sys.stdin.readline())
arr = []
for _ in range(N):
arr.append(int(sys.stdin.readline()))
average = round(sum(arr)/N)
print(average)
# Average
arr.sort()
median = arr[N // 2]
print(median)
# Median
count_array = Counter(arr).most_common()
if len(count_array) > 1 and count_array[0][1] == count_array[1][1]:
print(count_array[1][0])
else:
print(count_array[0][0])
range = max(arr) - min(arr)
print(range)
저 콜렉션 저거는 더 찾아보고 알려드림. 한가지 확실한건 저게 있어서 그나마 코드가 저렇게 짧은겁니다. ㄹㅇ임.
평균: 다 더해서 배열 길이로 나눈다 중앙값: 배열을 정렬한 다음 배열 길이 // 2의 값을 가져온다. (길이가 5면 2, 7이면 3 이런 식. 파이썬은 0부터 센다) 최빈값: 어레이를 하나 만들건데 배열 안에 들어있는 값과 몇개인지가 들어가게 된다. 이 배열을 비교해서 옆에 있는 애랑 개수가 같으면 두번째 놈을 출력한다. 범위: 최댓값-최솟값(개심플)
엥? 배열이 어떻게 나오길래 그래요?
[(-2, 2), (-1, 2), (-3, 1)]
이렇게 나온다. 오른쪽 값이 개수인데, 그게 같은 것끼리 정렬하고 큰 그림을 그리는 식이라 저걸로 처리된 것.
참가자 인원 수와 상을 타는 인원 수, 그리고 점수가 주어졌을 때 점수가 가장 낮은 사람을 출력하시오
풀이
보통 커트라인이라고 하면 수능이나 모평때 많이 쓰는 말인데, 9월 모평 1등급 등급컷이 얼마더라~ 이런 식으로 쓴다. 수능이나 모평의 등급컷은 전체적인 난이도와 참여 인원에 따라 달라지기때문에 똑같이 50점을 받았어도 난이도가 핵불닭수능이면 등급이 높고, 물수능이면 등급 망한다.
아, 그거랑 별개로 이 문제는 대단히 쉽다. 입력받은 점수를 정렬하고 뒤에서 k번째인 원소를 뽑으면 되거든.
import sys
N, k = map(int,sys.stdin.readline().split())
x = list(map(int,sys.stdin.readline().split()))
def merge_sort(array):
if len(array) < 2:
return array
mid = len(array) // 2
low_arr = merge_sort(array[:mid])
high_arr = merge_sort(array[mid:])
# 일단 짼다
merged_arr = []
l = h = 0
while l < len(low_arr) and h < len(high_arr):
if low_arr[l] < high_arr[h]:
merged_arr.append(low_arr[l])
l += 1
else:
merged_arr.append(high_arr[h])
h += 1
merged_arr += low_arr[l:]
merged_arr += high_arr[h:]
return merged_arr
# 그리고 비교한다
print(merge_sort(x))
일단 잘못하면 응애 나 애기시간초과! 가 반길수도 있으니 빠름 빠름 빠름 LTE-빠름! 이라는 병합정렬을 가져와보자. 그리고 예시를 저기에 돌려보면
5 2
100 76 85 93 98
[76, 85, 93, 98, 100]
정답이 98인데... 뒤에서 두번째에 있다.
import sys
N, k = map(int,sys.stdin.readline().split())
x = list(map(int,sys.stdin.readline().split()))
def merge_sort(array):
if len(array) < 2:
return array
mid = len(array) // 2
low_arr = merge_sort(array[:mid])
high_arr = merge_sort(array[mid:])
# 일단 짼다
merged_arr = []
l = h = 0
while l < len(low_arr) and h < len(high_arr):
if low_arr[l] < high_arr[h]:
merged_arr.append(low_arr[l])
l += 1
else:
merged_arr.append(high_arr[h])
h += 1
merged_arr += low_arr[l:]
merged_arr += high_arr[h:]
return merged_arr
# 그리고 비교한다
print(merge_sort(x)[-k])
그래서 정렬하고 뒤에서 k번째인 수를 뽑으면 되기 때문에 -k로 인덱싱 해 주면 된다. (-1이 뒤에서 첫번째)
대신 시퀀스 정보와 이름을 입력받는 란은 수기로 입력할때만 받으므로(FASTA, Genbank는 그 안에 정보가 다 있어서 Biopython으로 가져올 수 있다) 그 부분에 대한 처리가 별도로 필요하다. 어려운 건 아니고, 라디오버튼이 활성되었을 때만 입력할 수 있게 해 주면 된다.
그... 왜 Searcher에서 라디오버튼 선택하면 값 전송하는 건 다들 아실거고... 이걸 Cutter에도 적용해야 한다. 물론 Finder에도 적용할거다. 이게 왜 필요하냐면 입력 수단이 라디오버튼이기 때문. 그리고 라디오버튼에 각각 딸려오는 요소가 있어서, 연결된 라디오버튼이 선택되지 않았을 때 비활성화도 시켜줘야 한다.
이거 근데 Finder 가면서 소스 갈아엎음. ㅇㅇ
function activate() {
const checked_input = document.querySelectorAll('#typing')
const textarea = document.querySelectorAll('textarea')
for (let i = 0; i < checked_input.length; i++) {
if (checked_input[i].checked == true) {
textarea[i].disabled = false;
textarea[i].focus();
} else {
textarea[i].disabled = true;
}
}
}
function upfasta() {
const checked_fasta = document.querySelectorAll('#FASTA')
const upload_fasta = document.querySelectorAll('#input-FASTA')
for (let i = 0;i < checked_fasta.length;i++) {
if (checked_fasta[i].checked == true) {
upload_fasta[i].disabled = false;
}
else {
upload_fasta.disabled = true;
}
}
}
function upgen() {
const checked_gen = document.querySelectorAll('#Genbank')
const upload_gen = document.querySelectorAll('#input-Genbank')
for (let i = 0;i < checked_fasta.length;i++) {
if (checked_gen[i].checked == true) {
upload_gen[i].disabled = false;
}
else {
upload_gen.disabled = true;
}
}
}
소스가 왜 바꼈냐면 Finder 이식하면서 직접 입력/FASTA/Genbank가 아니라 커터/파인더로 나눴기 때문이다.
아무튼 Cutter에도 Searcher에 들어가는 라디오버튼이 일부 있다. NEB filter와 cut별 필터가 그거. 그니까 이번에 Ajax를 통해서 보내는 값은
1. 입력한 시퀀스 2. NEB filter 선택값 3. Cut 엔딩 선택값
이렇게 세 개다.
function cutFilter() {
const NEB_filter = document.getElementsByName('option_NEB');
const cut_filter = document.getElementsByName('option_cut');
keyword_val = document.getElementById('keywd').value;
for (let i = 0; i < NEB_filter.length; i++) {
if (NEB_filter[i].checked == true) {
NEB_filter_val = NEB_filter[i].value;
}
}
for (let i = 0; i < cut_filter.length; i++) {
if (cut_filter[i].checked == true) {
cut_filter_val = cut_filter[i].value
}
}
}
그리고 필터값과 달리 시퀀스는 이미 직접 입력하겠다고 라디오버튼으로 정한거라 걍 텍스트에리어 값을 보내면 된다.
참고로 얘는 로직 이식할 때 개고생했다. 값은 생각보다 수월하게 가져왔는데... Flask에 갖다놨더니 애가 전역변수를 인식을 못해서 함수로 뺐던거 다시 코드에 넣었다.
Finder
얘는 로직 이식은 생각보다 수월한데 반대로 JS쪽에서 개고생했다. 뭐가 불만인지 라디오버튼을 선택해도 세상에 활성이 안되잖어... 그래서 커터/파인더별로 묶었다. 용도별로 묶었더니 checked_input[i]가 체크 된 건 인식하는데 i번째 오브젝트 disabled가 안 풀렸거든...
const checked_input = document.querySelectorAll('#typing');
const input_enz = document.getElementsByName('enzyme');
const textarea = document.querySelectorAll('textarea');
const checked_fasta = document.querySelectorAll('#FASTA');
const checked_gen = document.querySelectorAll('#Genbank');
const NEB_filter = document.getElementsByName('option_NEB');
const cut_filter = document.getElementsByName('option_cut');
let sequence = '';
if (checked_input[1].checked == true) {
sequence = textarea[1].value;
}
for (let i = 0; i < NEB_filter.length; i++) {
if (NEB_filter[i].checked == true) {
NEB_filter_val = NEB_filter[i].value;
}
}
for (let i = 0; i < cut_filter.length; i++) {
if (cut_filter[i].checked == true) {
cut_filter_val = cut_filter[i].value
}
}
그것만 빼면 괜찮아서 입력 갈아엎고 스무th하게... 가긴 무슨... Finder는 이 효소가 이 시퀀스를 자르는가를 보는거라 저 필터들이 아예 없다. 그래서 저기 라디오버튼 걸려있는 필터들을 죄다 빼야 한다. 그러니까, 저기 있는 for문 두 개 얘기한거다.
Cutter와 Finder도 내일 이식하긴 할건데, 내일 이식하는 부분에서 FASTA/Genbank 파일 올리는거랑 결과 저장 기능은 빠진다. 그래서 원본 코드에 있던 with 어쩌고를 일단 작성 없이 서버로 보내서 텍스트 에리어에서 출력하는 걸 일차적으로 진행하고 저장-업로드 순으로 할 예정. FASTA/Genbank는 JS로 파일 받아서 파이썬으로 넘겨서 열 생각인데 그거 관련해서 로직 처리가 좀 필요하고(뼈대를 보면 라디오 버튼이다) 반대로 텍스트 파일은 파이썬에서 만들어서 JS로 넘길 생각이다. 텍스트 파일은 만들어준 거 받아서 버튼 누르면 다운로드 되게 하면 끝임.
대공사 전에...
일단 공사 전인 Searcher의 뼈대를 보자.
원래 코드에서 입력으로 받던 걸 죄다 라디오버튼으로 박아버렸다. JS파일의 임무는 일차적으로
1) 사용자가 입력한 검색어와 2) 라디오버튼의 값을 app.py(파이썬)으로 전달해주는
것이다. 로직은 뒤에서 돌 거니까 JS는
1) 로직을 돌리기 위해 필요한 변수들을 주고 2) 결과물을 받아서 출력한다
그리고 인자와 결과물의 전달을 Ajax가 한다. (얘때문에 jQuery 들어간거지 저거 빼고 어지간한 기능은 다 바닐라JS다)
function searchFilter() {
const search_method = document.getElementsByName('search_val');
console.log(search_method)
}
일단 이렇게 하면 값은 나오는데 문제는 뭐냐... 라디오버튼 중 선택된 값만 넘겨야 한다.
function searchFilter() {
const search_method = document.getElementsByName('search_val');
for (let i = 0;i < search_method.length;i++) {
if (search_method[i].checked == true) {
console.log(search_method[i].value)
}
}
}
그러니까 이 if를 세 개 줘야 한다.
function searchFilter() {
const search_method = document.getElementsByName('search_val');
const NEB_filter = document.getElementsByName('option_NEB');
const cut_filter = document.getElementsByName('option_cut');
for (let i = 0;i < search_method.length;i++) {
if (search_method[i].checked == true) {
console.log(search_method[i].value);
}
}
for (let i = 0;i < NEB_filter.length;i++){
if (NEB_filter[i].checked == true) {
console.log(NEB_filter[i].value);
}
}
for (let i = 0;i < cut_filter.length;i++){
if (cut_filter[i].checked == true) {
console.log(cut_filter[i].value)
}
}
}
왜 for+if가 3개인지 궁금하신 분들은 뼈대를 다시 보고 옵시다. 아무튼 그럼 Ajax를 불러보자.
let search_method_val = '';
let NEB_filter_val = '';
let cut_filter_val = '';
function searchFilter() {
const search_method = document.getElementsByName('search_val');
const NEB_filter = document.getElementsByName('option_NEB');
const cut_filter = document.getElementsByName('option_cut');
for (let i = 0; i < search_method.length; i++) {
if (search_method[i].checked == true) {
search_method_val = search_method[i].value;
}
}
for (let i = 0; i < NEB_filter.length; i++) {
if (NEB_filter[i].checked == true) {
NEB_filter_val = NEB_filter[i].value;
}
}
for (let i = 0; i < cut_filter.length; i++) {
if (cut_filter[i].checked == true) {
cut_filter_val = cut_filter[i].value
}
}
$.ajax({
type: "POST",
url: "/searcher",
data: {NEB_give: NEB_filter_val, cut_give: cut_filter_val, search_give: search_method_val},
success: function (response) {
alert(response['msg'])
}
})
}
위에서 기존 코드로 입력받던 것들을 라디오버튼으로 바꿨다고 했는데, 그거 말고도 변경점이 하나 더 있다. 원래 코드에서는 print문으로 출력해줬지만, 쟤는 app.py에서 다 만들고 나면 결과를 다시 JS에게 넘겨줘야 한다. 그러니까 단순히 print문으로 되어있던 걸 처리하는 과정이 필요하다.
@app.route('/searcher', methods=['POST'])
def searcher():
enzyme_table = pd.read_csv('/home/koreanraichu/restriction_merge.csv')
NEB_receive = request.form['NEB_give']
cut_receive = request.form['cut_give']
search_receive = request.form['search_give']
keywd_receive = request.form['keywd_give']
def SeqtoString(a):
a = enzyme_table.sequence[(enzyme_table['Enzyme'] == keywd_receive)]
a = a.to_string(index=False)
a = str(a).strip()
return a
def SitetoString(a):
a = enzyme_table.restriction_site[(enzyme_table['Enzyme'] == keywd_receive)]
a = a.to_string(index=False)
a = str(a).strip()
return a
def NEB_selling(a):
a = enzyme_table.NEB_sell[(enzyme_table['Enzyme'] == keywd_receive)]
a = a.to_string(index=False)
a = str(a).strip()
return a
# 함수 가즈아!!!
if cut_receive == 'sticky':
enzyme_table = enzyme_table[enzyme_table['cut_feature'] == 'Sticky']
enzyme_table.reset_index(inplace=True)
cut_feature = 'Sticky end'
elif cut_receive == 'blunt':
enzyme_table = enzyme_table[enzyme_table['cut_feature'] == 'Blunt']
enzyme_table.reset_index(inplace=True)
cut_feature = 'Blunt end'
elif cut_receive == 'nicked':
enzyme_table = enzyme_table[enzyme_table['cut_feature'] == 'Nicked']
enzyme_table.reset_index(inplace=True)
cut_feature = "Nicked"
else:
cut_feature = "W/O filter"
pass
if NEB_receive == 'NEB':
enzyme_table = enzyme_table[enzyme_table['NEB_sell'] == 'Yes']
enzyme_table.reset_index(inplace=True)
NEB_filter = 'NEB only'
else:
NEB_filter = 'All'
pass
if search_receive == 'name':
pass
elif search_receive == 'sequence':
pass
else:
pass
return jsonify({'msg': '입력되었습니다!'})
근데 함수 그냥 이렇게 써도 됨?
아무튼 그래서... print문을 전부 텍스트로 할당했다.
@app.route('/searcher', methods=['POST'])
def searcher():
enzyme_table = pd.read_csv('/home/koreanraichu/restriction_merge.csv')
NEB_receive = request.form['NEB_give']
cut_receive = request.form['cut_give']
search_receive = request.form['search_give']
keyword_receive = request.form['keyword_give']
def SeqtoString(a):
a = enzyme_table.sequence[(enzyme_table['Enzyme'] == keyword_receive)]
a = a.to_string(index=False)
a = str(a).strip()
return a
def SitetoString(a):
a = enzyme_table.restriction_site[(enzyme_table['Enzyme'] == keyword_receive)]
a = a.to_string(index=False)
a = str(a).strip()
return a
def NEB_selling(a):
a = enzyme_table.NEB_sell[(enzyme_table['Enzyme'] == keyword_receive)]
a = a.to_string(index=False)
a = str(a).strip()
return a
# 함수 가즈아!!!
if cut_receive == 'sticky':
enzyme_table = enzyme_table[enzyme_table['cut_feature'] == 'Sticky']
enzyme_table.reset_index(inplace=True)
cut_feature = 'Sticky end'
elif cut_receive == 'blunt':
enzyme_table = enzyme_table[enzyme_table['cut_feature'] == 'Blunt']
enzyme_table.reset_index(inplace=True)
cut_feature = 'Blunt end'
elif cut_receive == 'nicked':
enzyme_table = enzyme_table[enzyme_table['cut_feature'] == 'Nicked']
enzyme_table.reset_index(inplace=True)
cut_feature = "Nicked"
else:
cut_feature = "W/O filter"
pass
if NEB_receive == 'NEB':
enzyme_table = enzyme_table[enzyme_table['NEB_sell'] == 'Yes']
enzyme_table.reset_index(inplace=True)
NEB_filter = 'NEB only'
else:
NEB_filter = 'All'
pass
if search_receive == 'name':
find_seq = SeqtoString(keyword_receive)
Site_seq = SitetoString(keyword_receive)
NEB_sell = NEB_selling(keyword_receive)
result_iso = ''
result_neo = ''
result_iso_list = []
result_neo_list = []
Iso = []
Neo = []
message_give = "{0} | {1} | {2} | {3} | Input enzyme".format(keyword_receive, find_seq, Site_seq, NEB_sell)
for i in range(len(enzyme_table)):
DB_enzyme = str(enzyme_table['Enzyme'][i]).strip()
DB_seq = str(enzyme_table['sequence'][i]).strip().upper()
DB_site = str(enzyme_table['restriction_site'][i]).strip().upper()
if find_seq == str(DB_seq) and DB_enzyme != keyword_receive:
if Site_seq == DB_site:
Iso.append(DB_enzyme)
result_iso = "{0} | {1} | {2} | {3} | Isoschizomer".format(DB_enzyme, DB_seq, DB_site,
NEB_sell)
result_iso_list.append(result_iso)
# 인식하는 시퀀스와 자르는 방식이 같은 제한효소
elif Site_seq != DB_site:
Neo.append(DB_enzyme)
result_neo = "{0} | {1} | {2} | {3} | Neoschizomer".format(DB_enzyme, DB_seq, DB_site,
NEB_sell)
result_neo_list.append(result_neo)
# 인식하는 시퀀스는 같으나 자르는 방식이 다른 제한효소
elif find_seq == str(DB_seq) and DB_enzyme == keyword_receive:
pass
else:
pass
elif search_receive == 'sequence':
find_seq = keyword_receive
message_give = ("Searched by: {0}".format(keyword_receive))
for i in range(len(enzyme_table)):
DB_enzyme = str(enzyme_table['Enzyme'][i]).strip()
DB_seq = str(enzyme_table['sequence'][i]).strip().upper()
DB_site = str(enzyme_table['restriction_site'][i]).strip().upper()
DB_NEB = str(str(enzyme_table['NEB_sell'][i]).strip())
if find_seq == DB_seq:
print("{0} | {1} | {2} | {3}".format(DB_enzyme, DB_seq, DB_site, DB_NEB))
else:
enzyme_RE = keyword_receive
enzyme_RE = enzyme_RE.upper()
enzyme_RE_2 = '^' + enzyme_RE
message_give = ("Enzyme with start with {0}".format(enzyme_RE))
for i in range(len(enzyme_table)):
DB_enzyme = str(enzyme_table['Enzyme'][i]).strip()
DB_seq = str(enzyme_table['sequence'][i]).strip().upper()
DB_site = str(enzyme_table['restriction_site'][i]).strip().upper()
DB_NEB = str(str(enzyme_table['NEB_sell'][i]).strip())
if re.search(enzyme_RE_2, DB_enzyme):
print("{0} | {1} | {2} | {3}".format(DB_enzyme, DB_seq, DB_site, DB_NEB))
return jsonify({'Message_give': message_give,'Result_iso':result_iso_list,'Result_neo':result_neo_list})
얘때문임. Ajax로 다시 JS로 보내려면 JSON파일로 만들어야 하거든… 그래서 결과를 딕셔너리로 만든 다름 JSON으로 넘길거다.
success : function (response) {
var message = response['Message_give']
var iso = response['Result_iso']
var neo = response['Result_neo']
let result = document.getElementById('search_result');
result.innerText = message;
for (let i = 0; i < iso.length; i++) {
console.log(iso[i])
result.innerText = iso[i]
}
for (let i = 0; i < neo.length; i++) {
console.log(neo[i])
result.innerText = neo[i]
}
}
근데 여기서 줄바꿈 안돼서 진짜 온갖가지 것들을 다 해봤음... 근데 이스케이프 문자 안먹지 백틱 안먹지 뭐 어쩌라는건지...ㅡㅡ
success : function (response) {
if (search_method_val == 'name') {
let message = response['doc_result']['Message_give'];
let iso = response['doc_result']['Result_iso'];
let neo = response['doc_result']['Result_neo'];
let result = document.getElementById('search_result');
result.innerHTML = message;
for (let i = 0; i < iso.length; i++) {
result.innerHTML = result.innerHTML.concat("\n", iso[i])
}
for (let i = 0; i < neo.length; i++) {
result.innerHTML = result.innerHTML.concat("\n", neo[i])
}
} else if (search_method_val == 'sequence') {
let message = response['doc_result']['Message_give'];
let same_seq = response['doc_result']['Result_seq'];
let result = document.getElementById('search_result');
result.innerHTML = message;
for (let i = 0; i < same_seq.length; i++) {
result.innerHTML = result.innerHTML.concat("\n", same_seq[i])
}
} else {
let message = response['doc_result']['Message_give'];
let spell = response['doc_result']['Result_spell'];
let result = document.getElementById('search_result');
result.innerHTML = message;
for (let i = 0; i < spell.length; i++) {
result.innerHTML = result.innerHTML.concat("\n", spell[i])
}
}
}
from flask import Flask,render_template
import pandas as pd
import re
from datetime import datetime
from argparse import FileType
import tkinter
from tkinter import filedialog
from Bio import SeqIO
import os
import platform
app = Flask(__name__)
@app.route('/')
def hello_world(): # put application's code here
return render_template('index.html')
if __name__ == '__main__':
app.run()
function bluegrape() {
let contents = document.querySelector('textarea')
const text = `내 고장 칠월은
청포도가 익어가는 시절
이 마을 전설이 주절이주절이 열리고
먼데 하늘이 꿈꾸며 알알이 들어와 박혀
하늘 밑 푸른 바다가 가슴을 열고
흰 돛단배가 곱게 밀려서 오면
내가 바라는 손님은 고달픈 몸으로
청포를 입고 찾아온다고 했으니
내 그를 맞아 이 포도를 따먹으면
두 손은 함뿍 적셔도 좋으련
아이야 우리 식탁엔 은쟁반에
하이얀 모시 수건을 마련해두렴`
contents.textContent = text
}
그 다음 자바스크립트(스크립트 파일)로 넘어가서 함수를 이렇게 짜 주면 된다. (길이 제일 짧은게 저거) textarea를 가져와서 해당 텍스트를 컨텐츠로 채우는 식.
워드클라우드부터 해서 진짜 얘까지 개 노가다의 연속이었음... 그래도 이거 미리 해놔서 Entrez는 입력부만 조금 수정하면 됩니다.
워드클라우드 경로 썰은 다른 포스트에서 풀어드림.
이게 길어서 한번에 캡처 못했음다.
입력부
입력부의 각 기능
1. 텍스트 입력란(샘플 텍스트도 있음): 샘플 텍스트는 현재 한글 두개(별 헤는 밤, 청포도)랑 영어 하나(미정)를 생각중이며, 버튼을 누르면 샘플 텍스트가 텍스트에리어에 입력된다. 길면 잘 뽑히긴 한데 영어는 뭘 해야 할 지 모르겠음. 아니 그렇다고 하이 잭 하이 마이크 하와유 이딴걸 할 순 없잖수. 논문 해 논문 2. 컬러맵 선택(이따 이미지 올려드림): 이건 밑에 털어드림. 3. 마스킹이미지 ㄱㄱ: 이미지 안 쓴다는 선택지는 없음. 4. 언어 선택(한국어는 konlpy 한번 거쳐야 함)
즉 여기서 입력을 받아서 파이썬으로 전달하는 요소는
1. 워드클라우드를 만들 텍스트 2. 컬러맵 3. 글꼴(...) 4. 마스킹 이미지 5. 한글이냐? (제일 간단)
이다.
출력부
출력부는 개 심플하다. 저기에 워드클라우드 이미지가 뜨고 다운로드 버튼을 누르면 다운로드가 된다. 즉, 작업이 다 끝나서 이미지가 나오게 되면 파이썬에서 여기로 주면 된다.
컬러맵
참고로 이거 일부분이다. matplotlib 컬러맵이 진짜 개많음... 살려주세요... 사랑해요 그리드뷰
워드클라우드를 만드는 소스 코드가 파이썬인데, 파이썬에서 워드클라우드를 만들 때 컬러맵은 matplotlib의 컬러맵을 가져온다. 그니까 저 컬러맵 matplotlib으로 그래프 그릴 때도 쓸 수 있다. 그래프만으로 논문이 컬러풀해지는 기적 저걸 근데 입력받으면 분명 오타냄... 그리고 오타나면 워드클라우드 에러뜸... 그래서 저걸 어쩔 수 없이 라디오버튼으로 박아놨다.
그리고 라디오버튼에 할당된 value가 저 이름 그대로 들어가기 때문에 선택하고 OK만 하면 value 그대로 넘겨서 하면 된다.
from flask import Flask, render_template
from Bio import Entrez
from wordcloud import WordCloud
from wordcloud import STOPWORDS
import matplotlib.pyplot as plot
from PIL import Image
import numpy as np
from argparse import FileType
import tkinter
from tkinter import filedialog
import re
from konlpy.tag import Okt
import nltk
okt=Okt()
app = Flask(__name__)
@app.route('/')
def hello_world(): # put application's code here
return render_template('index.html')
if __name__ == '__main__':
app.run()
모듈 정모 들어간 것 같다면 정상… 차후 로직 만지면서 저기 말고 개별 코드에 들어갈수도 있다. 참고로 본인은 파이썬 모듈 중구난방인 거 싫어함.
블로그를 좀 오래 전부터 보신 분들은 아시겠지만 깃헙에 유일하게 폴더로 분리된 대형 프로젝트가 두 개 있다. 첫번째가 프로젝트 제한효소, 두번째가 프로젝트 워드클라우드. 근데 이게 쌩코드가 올라와서 이걸 복사해서 수정해서 또 올려야되는데 귀찮아요... 그래서 Flask로 웹이랑 묶으려고 생각중이다. (프로젝트 제한효소는 DB로 쓰는 csv파일도 포함)
근데 또 고려할 게 생겼어...OTL
1) 코드 통합 문제 프로젝트 워드클라우드의 경우 한글/영어/Entrez 세 개의 코드가 있고, 프로젝트 제한효소에는 커터/파인더/서쳐 세 개의 코드가 있다. 프로젝트 제한효소는 세 코드가 사용 목적이 다 달라서 통합은 안되고, 한 페이지 안에서 세개를 다 띄우려면 아코디언 패널이나 탭 메뉴가 있어야 한다. 워드클라우드도 Entrez는 입력 인자가 달라서 분리해야 하고, 한글과 영어는 언어에 따른 처리 인자만 달라서 코드를 통합하고 언어 선택을 라디오버튼으로 줄 수 있다. (페이지를 합칠거면 역시나 탭이나 아코디언 패널이 필요하다)
사실 이 부분은 별로 고민을 안한게 우리에겐 개쩌는 부트스트랩이 있다.
2) 파일 브라우징 문제 내가 저걸 돌리는 OS가 리눅스라 이미지 브라우저나 글꼴 폴더를 다 고정해놓고 쓰고 있는데, 일단 이게 왜 문제냐면, 코드를 돌리는 OS가 리눅스라 저장 경로가 리눅스 위주이다. (윈도우는 홈이 없음) 리눅스는 글꼴도 저장해두는 폴더 가서 ttf나 otf 선택해서 쓰면 된다. 그럼 윈도우는요? 윈도우는 글꼴 어떻게 고르는지 모른다.
프로젝트 제한효소도 FASTA, Genbank파일때문에 브라우저를 띄워야 하고, 워드클라우드는 마스킹 이미지때문에 브라우저를 띄워야 한다. OS 정보를 가져올 수 있으면 거기에 맞춰서 home이건 C드라이브건 띄우면 장땡. 저장은 브라우저 설정 따라가게 하면 된다.
3) 입력 인자 여기서부터는 프로젝트 워드클라우드의 문제. (제한효소는 저 두개만 처리하면 일단 끝난다)
Entrez는 Pubmed에서 논문 검색할 때 쓰는 검색어(A[TITLE] and B[TITLE])를 입력받기 때문에 그 부분에 대한 안내만 하면 된다. 문제는 그냥 워드클라우드인데, 이쪽은 현재 While True로 여러개 입력을 받고 있다. (하나만 써도 되긴 된다) IDE에서는 텍스트 편집이 어려워서 이렇게 했다.
현재 가장 좋은 방법은 textarea를 크게 주고 하나만 입력받는 걸로 제한하는 것. While True 걸어놓고 거기에 대해 안내하는것보단 이게 나을 듯 하다. 샘플 텍스트로 한글 워드클라우드는 윤동주-별 헤는 밤을 제공할 예정. 그게 제일 이쁘게 나왔다.
4) 글꼴 현재 워드클라우드 코드는 내가 보유한 글꼴을 선택해서 적용하고 있다. 그런데 다른 사람들이 내 글꼴을 쓸 수는 없고... 구글 웹폰트나 텍스트 에디터의 글꼴 선택 메뉴같은 게 있으면 좋을 것 같은데 그걸 어떻게 하는지를 몰라서, 이쪽으로는 더 알아봐야 할 듯 하다.
각각 이렇게 쓰면 된다. 근데 이렇게만 해 두면 왜 쟤가 빠른지 체감이 잘 안 올 것이다.
우리가 input()을 쓸 때는 저렇게 쓰는 것 보단
input('메시지를 입력해주세요')
이런 식으로 쓴다. 이 때, 저 안에 있는 텍스트를 '프롬프트 메시지'라고 한다. 그럼 sys.stdin.readline()도 프롬프트 메시지가 있나요? 놉. 이게 sys.stdin.readline()이 input()보다 빠른 이유 중 하나다. 엥? 이유가 또 있어요? 예.
파이썬에서는 입력을 받을 때 개행 문자(\n)가 같이 딸려온다. input()은 개행 문자를 떼버리는데 sys.stdin.readline()은 개행문자를 안 떼고 그냥 받는다. 그래서 sys.stdin.readline()으로 받고 나면 항상 rstrip()이나 strip() 메소드가 따라와야 한다.
a = sys.stdin.readline().rstrip()
a = sys.stdin.readline().strip()
그러니까 이렇게 써야 개행 문자를 떼고 입력을 받는다. 정확히 말하자면 rstrip()은 개행문자를 떼 주고, strip()은 공백을 떼버린다.
즉, sys.stdin.readline()이 빠른 이유는
1. 개행문자를 안 떼고
2. 프롬프트 메시지도 안 받기 때문
이라고 할 수 있다.
참고로 일부 IDE나 에디터에서 sys.stdin.readline()은 안먹으니 시간이 정말 급한거다 잘못하면 응애 나 애기시간초과! 가 반긴다 이런거 아님 걍 인풋 쓰자. 대표적인 예로 Jupyter에서는 sys.stdin.readline()이 입력을 안 받고(그냥 안받음) Spyder에서는 형변환 에러가 뜬다. 그냥 런던올림픽 펜싱심판 데려오면 안되나아니 그냥 파이참을 써 파이참은 안해봤는데 VScode는 sys.stdin.readline()이 먹힌다.
JSON은 JavaScript Object Notation을 줄인 것으로, 보통 제이슨으로 읽는다. 일반적으로 클라이언트가 서버에서 가져오는 데이터는 이 형식이고, 현재 표준 형식으로 많이 쓰이고 있다. 원류는 자바스크립트지만 현재는 언어 독립적인 개별 포맷. 그러니까 누가 제이슨 파일 주세요 하면 JSON파일 달라는거지 제이슨씨 파일 주세요가 아니다남의 파일을 왜 달라고 해