In [1]:
Celsius = [36.2, 36.7, 47.3, 17.8]
위 리스트를 이용하여 화씨 온도로 이루어진 리스트를 구현하는 방법은 아래와 같다.
In [2]:
Fahrenheit = [1.8 * C + 32 for C in Celsius]
Fahrenheit
Out[2]:
In [3]:
colors = ["red", "purple", "yellow", "blue", "green"]
things = [ "triangle", "rectangle", "pentagon" ]
(모양, 색깔) 형태의 튜플들의 가능한 모든 조합을 갖는 리스트를 구현하려면 다음과 할 수 있다. 총 15가지의 조합이 가능하다.
In [4]:
all_combination = [(x, y) for x in things for y in colors]
all_combination
Out[4]:
온라인 상의 아래 사이트에는 미국 해양대기청(NOAA)에서 수집하는 9천여 개의 세계 주요 도시들의 날씨 정보를 기상관측센터별 텍스트 파일로 저장되어 있다.
http://weather.noaa.gov/pub/data/observations/metar/decoded/
예를 들어 평택 기상관측센터의 날씨 정보는 RKSG.TXT
파일에 저장되어 있다.
즉, 아래 링크를 누르면 평택의 현재 날씨 정보를 확인할 수 있다.
http://weather.noaa.gov/pub/data/observations/metar/decoded/RKSG.TXT
Lab-07의 연습1에서 특정 기상관측센터에서 수집한 날씨정보에서 섭씨온도를 추출하는 방법을 다루었는데
이제는 NOAA 사이트에서 제공하는 모든 도시들의 섭씨온도 정보를 쉽게 확인해주는 함수
city_temperature
를 구현하고자 한다. 즉, 아래처럼 작동해야 한다.
city_temperature('RKSG') = 22
city_temperature('RKSI') = 20
참고로 NOAA에서 제공하는 한국에 위치한 기상관측센터별 코드는 아래 사이트에서 확인할 수 있으며,
예를 들어 인천공항에 위치한 기상관측센터의 코드는 RKSI
임을 사이트 주소를 통해 확인할 수 있다.
먼저 Lab-07의 연습 1에서는 평택의 온도정보를 확인하는 방법을 구현하였으며, 이에 대한 하나의 견본 답안은 아래와 같다.
def NOAA_string():
url = "http://weather.noaa.gov/pub/data" +\
"/observations/metar/decoded/RKSG.TXT"
noaa_data_string = urllib.urlopen(url).read()
return noaa_data_string
def NOAA_temperature(s):
L = s.split('\n')
Line7 = L[6].split()
print(str(int(Line7[-2][1:])) + " C")
이제 아래 명령어를 실행하면 평택의 섭씨온도를 확인할 수 있다.
NOAA_temperature(NOAA_string())
위 견본답안에서 사용된 NOAA_temperature
함수의 코드에서 Line7
변수의 값을 정의할 때
온도 정보가 해당 파일의 7번 줄에 있음을 가정한다.
그런데 이런 가정은 매우 위험하다.
실제로 코드명이 CWHP
인 기상관제센터에서 확인된 날씨정보에서 온도 정보는 5번 줄에 적혀있다.
http://weather.noaa.gov/pub/data/observations/metar/decoded/CWHP.TXT
또한 A302
코드명을 가진 기상관제센터의 정보에서는 Temperature
, 즉 온도 정보 자체가 없다.
http://weather.noaa.gov/pub/data/observations/metar/decoded/A302.TXT
city_temperature
함수를 구현하기 위해서는 NOAA_temperature
함수를 특정 숫자에 의존하지 않도록 구현해야 한다.
Temperature
정보의 유무를 먼저 확인한 후에, 있다면 몇 번 줄에 있는지를 알아내서 활용해서 NOAA_temperature
함수를 구현하라. 온도정보가 존재하지 않으면 'Info NA'
를 프린태해야 한다. NA
는 Not Available
의 줄임말이다.
예를 들어
In [2]: NOAA_temperature(NOAA_string('RKSG'))
Out[2]: 22
In [3]: NOAA_temperature(NOAA_string('RKSI'))
Out[3]: 20
In [3]: NOAA_temperature(NOAA_string('A302'))
Out[3]: 'Info NA'
의 결과를 얻을 수 있어야 한다. 즉, city_temperature
함수는 아래와 같이 정의될 수 있다.
def city_temperature(s):
return NOAA_temperature(NOAA_string(s))
특정 단어가 몇 번 줄에 나타나는지를 확인하기 위해서는 enumerate
함수와 find
메소드를 활용할 수 있다. enumerate
함수의 활용법은 아래와 같다.
In [1]: for index, line in enumerate(['a', 'b', 'c', 'd']):
print("{} {}").format(index, line)
0 a
1 b
2 c
3 d
In [5]:
import urllib
url = "http://weather.noaa.gov/pub/data" +\
"/observations/metar/decoded/"
def NOAA_string(s):
noaa_data_string = urllib.urlopen(url + s + '.TXT').read()
return noaa_data_string
def NOAA_temperature(s):
if s.find("Temperature") == -1:
return "Info NA"
else:
L = s.split('\n')
for index, line in enumerate(L):
if line.find("Temperature") == -1:
pass
else:
break
temp_line = L[index].split()
return int(float(temp_line[-2][1:]))
def city_temperature(s):
return NOAA_temperature(NOAA_string(s))
print(city_temperature('A302'))
print(city_temperature('RKSG'))
위 연습문제에서는 NOAA 사이트에 저장된 문서를 필요할 때마다 하나씩 확인하는 함수를 구현하였다. 이번에는 NOAA 사이트가 제공하는 모든 기상관측센터의 온도정보를 한꺼번에 가져와서 코드명과 섭씨온도의 쌍으로 이루어진 시퀀스 자료형을 만드는 방법을 배운다.
먼저, 다시 한 번 아래 사이트를 방문해보자. 그러면 ****.TXT
형식의 이름을 가진 수천 개의 파일을 확인할 수 있다.
http://weather.noaa.gov/pub/data/observations/metar/decoded/
정확히 9천여개의 파일이 들어 있다. (어떻게 알 수 있을까?) 그렇다면 위 온라인상에 위치한 폴더 안에 저장되어 있는 모든 파일들의 파일명으로 이루어진 리스트를 만들 수는 없을까가 당연히 궁금해진다.
앞서 인터넷 특정 주소에 저장된 텍스트파일을 읽어드리기 위해 urllib
모듈의 urlopen
함수를 사용하였는데,
이제는 온라인 상의 특정 폴더 안에 저장된 파일들의 이름을 불러오는 함수가 필요하다.
requests
모듈에 있는 get
이라는 함수가 urlopen
함수와 비슷한 기능을 수행하며 온라인 상의 특정 폴더에 대한 정보를 가져온다. 아래와 같이 실행하면 위 사이트에 연결된 폴더에 대한 정보를 통으로, 즉 하나의 커다란 문자열로 가져온다.
In [6]:
import requests
NOAA = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/'
city_name_info = str(requests.get(NOAA).text)
In [7]:
type(city_name_info)
Out[7]:
아래 명령어를 실행하면
print(city_name_info)
굉장히 긴 내용을 확인할 수 있다. (9천여줄이나 된다.)
예를 들어 처음 부분은 아래와 같이 시작한다.
======
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /pub/data/observations/metar/decoded</title>
</head>
<body>
<h1>Index of /pub/data/observations/metar/decoded</h1>
<pre><img src="/icons/blank.gif" alt="Icon "> <a href="?C=N;O=D">Name</a> <a href="?C=M;O=A">Last modified</a> <a href="?C=S;O=A">Size</a> <a href="?C=D;O=A">Description</a><hr><img src="/icons/back.gif" alt="[DIR]"> <a href="/pub/data/observations/metar/">Parent Directory</a> -
<img src="/icons/text.gif" alt="[TXT]"> <a href="A302.TXT">A302.TXT</a> 09-Sep-2011 16:09 329
<img src="/icons/text.gif" alt="[TXT]"> <a href="AABS.TXT">AABS.TXT</a> 16-Nov-2008 19:09 248
<img src="/icons/text.gif" alt="[TXT]"> <a href="AAXX.TXT">AAXX.TXT</a> 12-Oct-2015 21:02 334
======
html을 다뤄본 적이 있다면 위 내용의 의미를 알 수 있을 것이지만 여기서는 중요하지 않다. 우리에게 필요한 정보가 어디에 있는지만 확인할 수 있으면 충분하기 때문이다.
자세히 보면 밑에서부터 세 줄은 동일한 모양을 갖추고 있다. 다만 ****.TXT
라는 파일명만 다를 뿐이며, 바로 그 파일명들이 우리가 원하는 정보이다.
이미 말했듯 그러한 줄이 9천여 개가 존재한다.
자 이제 city_name_info
파일을 줄단위로 쪼개보자.
In [8]:
city_name_info_line = city_name_info.split('\n')
In [9]:
type(city_name_info_line)
Out[9]:
In [10]:
len(city_name_info_line)
Out[10]:
처음 몇 줄만을 확인해보자.
In [11]:
city_name_info_line[:10]
Out[11]:
9번 줄부터 도시 정보에 대한 파일들 정보가 들어있음을 확인 할 수 있다. 처음 10개 도시 정보를 확인해보자.
In [12]:
city_name_info_line[8:18]
Out[12]:
아래 사이트에 보이는 상위 10개 도시의 코드명 파일과 일치하는 내용을 담고 있음을 확인할 수 있다.
http://weather.noaa.gov/pub/data/observations/metar/decoded/
마지막 몇 줄을 확인해도 마찬가지이다.
In [13]:
city_name_info_line[-10:]
Out[13]:
마지막 4줄은 역시 html 관련 정보이며 기상관측센터와 아무 상관이 없다.
따라서 city_name_info_line
의 8번줄에서 끝에서 5번줄까지가 도시 정보를 담은 파일들이다.
city_name_info_line[8:-4]
In [14]:
city_name_info_line[-10:-4]
Out[14]:
이제 city_line_info_line
의 각 항목에서 각 도시의 코드명을 추출하기는 쉽다.
예를 들어 첫 번째 기상관측센터의 코드명인 A302
을 8번 줄에서 다음과 같이 추출할 수 있다.
In [15]:
A302_line = city_name_info_line[8]
num = A302_line.find('.TXT')
print(A302_line[num-4 : num])
위 코드에 for
문을 적용하면 모든 도시의 코드명으로 이루어진 리스트를 생성할 수 있다.
In [16]:
city_codes = []
for line in city_name_info_line[8:-4]:
num = line.find('.TXT')
city_codes.append(line[num-4 : num])
위 코드를 리스트 조건제시법으로 구현할 수도 있다.
In [17]:
city_codes = [line[line.find('.TXT')-4 : line.find('.TXT')] for line in city_name_info_line[8:-4]]
In [18]:
city_codes[-10:]
Out[18]:
In [19]:
len(city_codes)
Out[19]:
현재 8947개의 기상관측센터의 코드명이 저장되어 있음을 확인할 수 있다.
주의: 기상관측센터의 정확한 개수는 변할 수 있다.
처음 10개 기상관측센터 코드명은 아래와 같다.
In [20]:
city_codes[:10]
Out[20]:
다음 단계는 코드명과 해당 도시의 온도를 쌍으로 갖는 시퀀스 자료형을 만드는 일이다. 여기서는 리스트를 이용하는 방식과 해시 테이블을 이용하는 방식을 다룬다.
In [21]:
def city_temp_list(num):
L = []
for city in city_codes[:num]:
temp = city_temperature(city)
L.append([city, temp])
return L
이제 처음 30개 기상관측센터의 정보를 저장해보자.
In [22]:
List_sample30 = city_temp_list(30)
그런 다음 특정 기상관측센터에서 측정한 온도를 확인해보자. 먼저 처음 30개 기상관측센터의 리스트는 아래와 같다.
즉, 처음 30개 기상관측센터의 정보를 확인하는 데에 10여초 정도 걸린다. 9000여 개 전체를 확인하고자 한다면 4000초, 즉 한 시간 이상 걸린다는 계산이다.
In [23]:
city_codes[:30]
Out[23]:
쌍들의 리스트에서 특정 코드명의 온도를 찾아내는 코드는 예를 들어 아래와 같다.
In [24]:
def list_search(x, xs):
for y in xs:
if x == y[0]:
return y[1]
else:
pass
list_search
함수의 작동시간을 측정하려면 아래와 같이 실행하면 된다.
In [25]:
%time list_search('BDAB', List_sample30)
Out[25]:
In [26]:
%time list_search('ABLC', List_sample30)
In [27]:
%time list_search('ABBN', List_sample30)
Out[27]:
In [28]:
%time for code in city_codes[:30]: list_search(code, List_sample30)
30개 정보를 모두 확인하는데 총 60 마이크로 초(microsecond)가 걸린다.
참고로 1 마이크로 초는 10 ** -6
초이다.
주의: 시간은 사용하는 컴퓨터의 성능과 환경에 크게 차이가 날 수 있다.
In [29]:
def cities(xs):
L = []
for x in xs:
L.append(x[x.find('.TXT') - 4: x.find('.TXT') + 4])
return L
In [30]:
def city_temp_hash(num):
H = {}
for city in city_codes[:num]:
temp = city_temperature(city)
H[city] = temp
return H
In [31]:
Hash_sample30 = city_temp_hash(30)
In [32]:
Hash_sample30
Out[32]:
In [33]:
%time Hash_sample30['BDAB']
Out[33]:
In [34]:
%time Hash_sample30['AVLC']
Out[34]:
In [35]:
%time Hash_sample30['ABBN']
Out[35]:
In [36]:
%time for code in city_codes[:30]: Hash_sample30[code]
해시 테이블을 이용하였을 경우 30개의 샘플 정보를 모두 확인하는 데에 12 마이크로 초가 걸린다. 이는 앞서 리스트를 이용한 경우보다 몇 배가 빠른 속도이다. 리스트의 길이가 길어질 수록 속도차이는 더욱 벌어질 것이다. 각자 확인할 수 있을 것이다.
In [37]:
def city_temp_list_F(num):
F = []
for center in city_temp_list(num):
if isinstance(center[1], int):
F.append([center[0], float(1.8 * center[1] + 32)])
else:
F.append([center[0], 'Info NA'])
return F
In [38]:
city_temp_list_F(10)
Out[38]: