15 Tue

TIL

프로그래머스 AI 스쿨 1기

3주차 DAY 2

I. pandas 시작하기

Prerequisite : Table

  • 행과 열을 이용해서 데이터를 저장하고 관리하는 자료구조(컨테이너)

  • 주로 행은 개체, 열은 속성을 나타냄

Pandas 설치하기

pip install pandas

Pandas 시작하기

import pandas를 통해서 진행 pandas는 관습적으로 pd 축약해서 사용

import pandas as pd

II. pandas로 1차원 데이터 다루기 - Series

Series?

  • 1-D labeled array

  • 인덱스를 지정해줄 수 있음

s = pd.Series([1, 4, 9, 16, 25])

s
0     1
1     4
2     9
3    16
4    25
dtype: int64
t = pd.Series({'one':1, 'two':2, 'three':3, 'four':4, 'five':5})

t
one      1
two      2
three    3
four     4
five     5
dtype: int64

Series + Numpy

  • Series는 ndarray와 유사하다!

s[1]
4
t[1]
# 딕셔너리도 인덱스로 접근 가능하다
2
t[1:3]
# 슬라이싱도 가능하다
two      2
three    3
dtype: int64
s [s > s.median()]
# 자기 자신의 median(중앙값)보다 큰 값들만 출력
# pandas는 이러한 내장 함수가 존재함
3    16
4    25
dtype: int64
s[[3, 1, 4]]
3    16
1     4
4    25
dtype: int64
import numpy as np

np.exp(s)
0    0.265717
1    2.335211
2    0.802393
3    1.888751
4    0.897110
Name: 임의의 난수, dtype: float64
s.dtype
dtype('int64')

pandas와 numpy의 유사성을 볼 수 있음

Series + dict

  • series는 dict와 유사하다

t['one']
1
# Series에 값 추가

t['six'] = 6
t
one      1
two      2
three    3
four     4
five     5
six      6
dtype: int64
'six' in t
True
'seven' in t
# t['seven'] : error ocuurence
False
t.get('seven')
# no return
t.get('seven', 0)
# if seven not in t then return 0
0

Series에 이름 붙이기

  • name속성을 가지고 있다.

  • 처음 Series를 만들 때 이름을 붙일 수 있다.

s = pd.Series(np.random.randn(5), name="random_nums")
s

Numpy의 Random 함수 3가

0   -1.325324
1    0.848102
2   -0.220157
3    0.635916
4   -0.108576
Name: random_nums, dtype: float64

dtype 뿐만 아니라 Name도 출력

s.name = "임의의 난수"
s
0   -1.325324
1    0.848102
2   -0.220157
3    0.635916
4   -0.108576
Name: 임의의 난수, dtype: float64

III. Pandas로 2차원 데이터 다루기 - dataframe

dataframe?

  • 2-D labeled table

  • 인덱스를 지정할 수도 있음

  • 표나 테이블등의 2차원 데이터를 표현하기에 리스트는 부적합

  • 따라서, 딕셔너리 사용

d = {"height":[1, 2, 3, 4], "weight":[30, 40, 50, 60]}

df = pd.DataFrame(d) #DataFrame의 D와 F가 대문자여야함

df

height

weight

0

1

30

1

2

40

2

3

50

3

4

60

## dtype 확인
df.dtypes
height    int64
weight    int64
dtype: object

From CSV to DataFrame

  • CSV : Comma Separated Value

  • pandas는 csv 파일을 dataframe화 할 수있는 함수를 제공

  • .read_csv()를 이용

  • 실제로 csv는 ,로 구분된 데이터들로 이루어져있음

  • csv의 각 첫줄에는 각 컬럼에 해당하는 항목이름

# 동일 경로에 country_wise_latest.csv가 존재해야함

covid = pd.read_csv("./country_wise_latest.csv")
covid

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

0

Afghanistan

36263

1269

25198

9796

106

10

18

3.50

69.49

5.04

35526

737

2.07

Eastern Mediterranean

1

Albania

4880

144

2745

1991

117

6

63

2.95

56.25

5.25

4171

709

17.00

Europe

2

Algeria

27973

1163

18837

7973

616

8

749

4.16

67.34

6.17

23691

4282

18.07

Africa

3

Andorra

907

52

803

52

10

0

0

5.73

88.53

6.48

884

23

2.60

Europe

4

Angola

950

41

242

667

18

1

0

4.32

25.47

16.94

749

201

26.84

Africa

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

182

West Bank and Gaza

10621

78

3752

6791

152

2

0

0.73

35.33

2.08

8916

1705

19.12

Eastern Mediterranean

183

Western Sahara

10

1

8

1

0

0

0

10.00

80.00

12.50

10

0

0.00

Africa

184

Yemen

1691

483

833

375

10

4

36

28.56

49.26

57.98

1619

72

4.45

Eastern Mediterranean

185

Zambia

4552

140

2815

1597

71

1

465

3.08

61.84

4.97

3326

1226

36.86

Africa

186

Zimbabwe

2704

36

542

2126

192

2

24

1.33

20.04

6.64

1713

991

57.85

Africa

187 rows × 15 columns

Pandas 활용 1. 일부분만 관찰하기

head(n) : 처음 n개의 데이터 참조

# 위에서부터 5개를 관찰하는 방법(함수)

covid.head(5)

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

0

Afghanistan

36263

1269

25198

9796

106

10

18

3.50

69.49

5.04

35526

737

2.07

Eastern Mediterranean

1

Albania

4880

144

2745

1991

117

6

63

2.95

56.25

5.25

4171

709

17.00

Europe

2

Algeria

27973

1163

18837

7973

616

8

749

4.16

67.34

6.17

23691

4282

18.07

Africa

3

Andorra

907

52

803

52

10

0

0

5.73

88.53

6.48

884

23

2.60

Europe

4

Angola

950

41

242

667

18

1

0

4.32

25.47

16.94

749

201

26.84

Africa

tail(n) : 마지막 n개의 데이터를 참조

# 아래에서부터 5개를 관찰하는 방법(함수)

covid.tail(5)

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

182

West Bank and Gaza

10621

78

3752

6791

152

2

0

0.73

35.33

2.08

8916

1705

19.12

Eastern Mediterranean

183

Western Sahara

10

1

8

1

0

0

0

10.00

80.00

12.50

10

0

0.00

Africa

184

Yemen

1691

483

833

375

10

4

36

28.56

49.26

57.98

1619

72

4.45

Eastern Mediterranean

185

Zambia

4552

140

2815

1597

71

1

465

3.08

61.84

4.97

3326

1226

36.86

Africa

186

Zimbabwe

2704

36

542

2126

192

2

24

1.33

20.04

6.64

1713

991

57.85

Africa

Pandas 활용 2. 데이터 접근하기

  • df['column_name'] or df.column_name

covid['Active'].head(3)
0    9796
1    1991
2    7973
Name: Active, dtype: int64
covid.Active.head(3)
0    9796
1    1991
2    7973
Name: Active, dtype: int64

column name을 attribute로 접근할 때에는 spacebar가 적용된 변수명은 적용하지 못하는 차이가 있다.

ex) covid.Who Region O : covid["Who Region"] X : covid.Who Region

Honey Tip! Dataframe의 각 column은 "Series"이다!

covid['Confirmed'][0]
36263
covid['Confirmed'][1:5]
1     4880
2    27973
3      907
4      950
Name: Confirmed, dtype: int64

Pandas 활용 3. "조건"을 이용해서 데이터 접근하기

# 신규 확진자가 100명이 넘는 나라를 찾아보자!
# 해당하는 column은 New cases

covid[covid['New cases'] > 100].head(3)

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

0

Afghanistan

36263

1269

25198

9796

106

10

18

3.50

69.49

5.04

35526

737

2.07

Eastern Mediterranean

1

Albania

4880

144

2745

1991

117

6

63

2.95

56.25

5.25

4171

709

17.00

Europe

2

Algeria

27973

1163

18837

7973

616

8

749

4.16

67.34

6.17

23691

4282

18.07

Africa

# unique() : 자료의 범주를 중복 없이 출력
covid['WHO Region'].unique()
array(['Eastern Mediterranean', 'Europe', 'Africa', 'Americas',
       'Western Pacific', 'South-East Asia'], dtype=object)
# WHO 지역이 동남아시아인 나라 찾기
covid[covid['WHO Region'] == 'South-East Asia'].head(3)

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

13

Bangladesh

226225

2965

125683

97577

2772

37

1801

1.31

55.56

2.36

207453

18772

9.05

South-East Asia

19

Bhutan

99

0

86

13

4

0

1

0.00

86.87

0.00

90

9

10.00

South-East Asia

27

Burma

350

6

292

52

0

0

2

1.71

83.43

2.05

341

9

2.64

South-East Asia

Pandas 활용 4. 행을 기준으로 데이터 접근하기

# 예시 데이터 - 도서관 정보

books_dict = {"Available":[True, False, False], "Location":[102, 215, 323], " Genre":["Programming", "Physics", "Math"]}

books_df = pd.DataFrame(books_dict, index=["버그란 무엇인가", "두근두근 물리학", "미분해줘 홈즈"])

books_df

Available

Location

Genre

버그란 무엇인가

True

102

Programming

두근두근 물리학

False

215

Physics

미분해줘 홈즈

False

323

Math

인덱스를 이용해서 가져오기 : loc[row, col]

books_df.loc["버그란 무엇인가"]
Available           True
Location             102
 Genre       Programming
Name: 버그란 무엇인가, dtype: object
type(books_df.loc["버그란 무엇인가"])
pandas.core.series.Series
# "미분해줘 홈즈" 책이 대출 가능한지?

books_df.loc["미분해줘 홈즈", 'Available']
False

숫자 인덱스를 이용해서 가져오기 : `.iloc[rowidx, colidx]

# 인덱스 0행, 1열

books_df.iloc[0, 1]
102
# 인덱스 1행, 0~1열

books_df.iloc[1, 0:2]
Available    False
Location       215
Name: 두근두근 물리학, dtype: object

Pandas 활용 5. groupby

  • Split : 특정한 "기준"을 바탕으로 DataFrame을 분할

  • Apply : 통계함수 - sum(), mean(), median(), - 을 적용해서 각 데이터를 압축

  • Combine : Apply된 결과를 바탕으로 새로운 Series를 생성 (group_key : applied_value)

.groupby()

covid.head(5)

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

0

Afghanistan

36263

1269

25198

9796

106

10

18

3.50

69.49

5.04

35526

737

2.07

Eastern Mediterranean

1

Albania

4880

144

2745

1991

117

6

63

2.95

56.25

5.25

4171

709

17.00

Europe

2

Algeria

27973

1163

18837

7973

616

8

749

4.16

67.34

6.17

23691

4282

18.07

Africa

3

Andorra

907

52

803

52

10

0

0

5.73

88.53

6.48

884

23

2.60

Europe

4

Angola

950

41

242

667

18

1

0

4.32

25.47

16.94

749

201

26.84

Africa

# WHO Region별 확진자수

# 1. covid에서 확진자 수 column만 추출한다
# 2. 이를 covid WHO Region을 기준으로 groupby한다

covid_by_region = covid['Confirmed'].groupby(by=covid["WHO Region"])

covid_by_region
<pandas.core.groupby.generic.SeriesGroupBy object at 0x00000205A35511C8>
covid_by_region.sum()
WHO Region
Africa                    723207
Americas                 8839286
Eastern Mediterranean    1490744
Europe                   3299523
South-East Asia          1835297
Western Pacific           292428
Name: Confirmed, dtype: int64
# 국가당 감영자 수

covid_by_region.mean()
WHO Region
Africa                    15066.812500
Americas                 252551.028571
Eastern Mediterranean     67761.090909
Europe                    58920.053571
South-East Asia          183529.700000
Western Pacific           18276.750000
Name: Confirmed, dtype: float64

Mission:

1. covid 데이터에서 100 case 대비 사망률(Deaths / 100 Cases)이 가장 높은 국가는?

covid.iloc[covid['Deaths / 100 Cases'].idxmax(), 0]
'Yemen'

2. covid 데이터에서 신규 확진자가 없는 나라 중 WHO Region이 'Europe'를 모두 출력하면?

Hint : 한 줄에 동시에 두가지 조건을 Apply하는 경우 Warning이 발생할 수 있습니다.

cond = covid[covid['New cases'] == 0]
cond[cond['WHO Region'] == 'Europe']

Country/Region

Confirmed

Deaths

Recovered

Active

New cases

New deaths

New recovered

Deaths / 100 Cases

Recovered / 100 Cases

Deaths / 100 Recovered

Confirmed last week

1 week change

1 week % increase

WHO Region

56

Estonia

2034

69

1923

42

0

0

1

3.39

94.54

3.59

2021

13

0.64

Europe

75

Holy See

12

0

12

0

0

0

0

0.00

100.00

0.00

12

0

0.00

Europe

95

Latvia

1219

31

1045

143

0

0

0

2.54

85.73

2.97

1192

27

2.27

Europe

100

Liechtenstein

86

1

81

4

0

0

0

1.16

94.19

1.23

86

0

0.00

Europe

113

Monaco

116

4

104

8

0

0

0

3.45

89.66

3.85

109

7

6.42

Europe

143

San Marino

699

42

657

0

0

0

0

6.01

93.99

6.39

699

0

0.00

Europe

157

Spain

272421

28432

150376

93613

0

0

0

10.44

55.20

18.91

264836

7585

2.86

Europe

3. 다음 데이터를 이용해 각 Region별로 아보카도가 가장 비싼 평균가격(AveragePrice)을 출력하면?

avocado = pd.read_csv("./avocado.csv")
avocado

Unnamed: 0

Date

AveragePrice

Total Volume

4046

4225

4770

Total Bags

Small Bags

Large Bags

XLarge Bags

type

year

region

0

0

2015-12-27

1.33

64236.62

1036.74

54454.85

48.16

8696.87

8603.62

93.25

0.0

conventional

2015

Albany

1

1

2015-12-20

1.35

54876.98

674.28

44638.81

58.33

9505.56

9408.07

97.49

0.0

conventional

2015

Albany

2

2

2015-12-13

0.93

118220.22

794.70

109149.67

130.50

8145.35

8042.21

103.14

0.0

conventional

2015

Albany

3

3

2015-12-06

1.08

78992.15

1132.00

71976.41

72.58

5811.16

5677.40

133.76

0.0

conventional

2015

Albany

4

4

2015-11-29

1.28

51039.60

941.48

43838.39

75.78

6183.95

5986.26

197.69

0.0

conventional

2015

Albany

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

18244

7

2018-02-04

1.63

17074.83

2046.96

1529.20

0.00

13498.67

13066.82

431.85

0.0

organic

2018

WestTexNewMexico

18245

8

2018-01-28

1.71

13888.04

1191.70

3431.50

0.00

9264.84

8940.04

324.80

0.0

organic

2018

WestTexNewMexico

18246

9

2018-01-21

1.87

13766.76

1191.92

2452.79

727.94

9394.11

9351.80

42.31

0.0

organic

2018

WestTexNewMexico

18247

10

2018-01-14

1.93

16205.22

1527.63

2981.04

727.01

10969.54

10919.54

50.00

0.0

organic

2018

WestTexNewMexico

18248

11

2018-01-07

1.62

17489.58

2894.77

2356.13

224.53

12014.15

11988.14

26.01

0.0

organic

2018

WestTexNewMexico

18249 rows × 14 columns

regions = avocado['region'].unique()
for region in regions:
    cond = avocado[avocado['region'] == region]
    print(avocado.iloc[cond['AveragePrice'].idxmax(),:])

오토마타와 컴파일러

CFG : removing ambiguous grammar

Context Free Grammar는 구문 분석을 하는데 있어서 효율을 상당히 떨어뜨리는 경우에 효율적인 구문분석이 이루어지도록 주어진 문법을 적당한 문법으로 바꾸는 문법 변환을 필요로 한다. 모호한 문법의 제거 방법은 다음과 같다.

  • 불필요한 생성규칙의 제거

  • ε-생성규칙의 제거

  • 단일 생성규칙의 제거

  • 좌인수분해

  • 좌재귀 제거

이를 구현하면 다음과 같다. (코드를 보기 전에 언을 하자면, 구현된 제거는 불필요한 생성규칙의 제거, ε-생성규칙의 제거, 단일 생성규칙의 제거 까지이며 이마저도 완벽하게 구현되지 않았다. 지금까지 찾은 반례로는 S -> ABC, A -> B | a, B -> C | b, C -> A | c 와 같은 순환 코드에 대해서 작동하지 않는다. 그 외에는 잘 작동한다고 생각이 들지만 오류를 발생시키는 반례가 충분히 있을 것이라 예상한다. 좌인수분해와 좌재귀제거는 코드가 너무 지저분해져 능력을 벗어나는 일이라고 생각해 포기했다.)

#
#   data = 2020.12.15
#   author = sangmandu at Dankook Univ.
#   program = Effective Context Free Grammar in that removing unnecessary(ambiguous) grammar
#   language = python
#

#
#   P = Set of grammar
#   S = StartSymbol
#   Z = S' (alternative S that express ε)
#   [A-Z] = Non terminal
#   [a-z][0-9][._()] = Terminal
#   ε = null
#

def initialization(P):
    dic = {}
    Vn, Vt = set(), set()
    for grammar in P:
        src, dst = grammar.split("->")
        Vn.add(src)
        dst = dst.split("|")
        dic.setdefault(src, [])
        for d in dst:
            dic[src].append(d)
            for letter in d:
                if letter.isupper():
                    Vn.add(letter)
                else:
                    Vt.add(letter)
    printResult(initialization.__name__, [dic, Vn, Vt])
    return dic, Vn, Vt

def refreshVnVt(dic):
    Vn, Vt = set(), set()
    for key, values in dic.items():
        Vn.add(key)
        for value in values:
            for letter in value:
                if not letter.isupper():
                    Vt.add(letter)
    return Vn, Vt


def checkNeed(dic, Vn, flag=1):
    stack = ['S']
    _Vn = set()
    while stack:
        nt = stack.pop(0)
        _Vn.add(nt)
        for symbol in dic[nt]:
            for letter in symbol:
                if letter.isupper() and letter not in _Vn and letter not in stack:
                    stack.append(letter)
    for nt in Vn - _Vn:
        del dic[nt]
    Vn, Vt = refreshVnVt(dic)
    if flag:
        printResult(checkNeed.__name__, [dic, Vn, Vt])
    return Vn, Vt

def checkNull(dic, Vn, Vt):
    if 'ε' not in Vt: return Vn, Vt
    _Nt = []
    for nt in Vn:
        if 'ε' in dic[nt]:
            _Nt.append(nt)

    size = 0
    while len(_Nt) != size:
        size = len(_Nt)
        for nt in _Nt:
            for key, value in dic.items():
                for v in value:
                    if nt in v and key not in _Nt:
                        _Nt.append(key)
    for key in Vn:
        stack = dic[key][:]
        dup = []
        while stack:
            value = stack.pop(0)
            if value == "ε":
                continue
            if not value.isupper():
                dic[key].append(value)

            for idx, letter in enumerate(value):
                if letter in _Nt and (value, idx) not in dup:
                    stack.append(value)
                    dup.append((value, idx))
                    stack.append(value[:idx]+value[idx+1:])
            else:
                if value and value not in dic[key]:
                    dic[key].append(value)

    for key, value in dic.items():
        dic[key] = list(set(value))
        while "ε" in dic[key]:
            dic[key].remove("ε")
        while "" in dic[key]:
            dic[key].remove("")

    dic["Z"], dic["S"] = dic["S"], ["Z", "ε"]
    Vn, Vt = refreshVnVt(dic)
    printResult(checkNull.__name__, [dic, Vn, Vt])
    return Vn, Vt

def checkNtoN(dic):
    from itertools import product
    temp = 0
    while True:
        length = sum([len(dic[x]) for x in dic.keys()])
        if temp == length: break
        temp = length
        replace = []
        for key, values in dic.items():
            for value in values:
                if all([letter.isupper() for letter in value]):
                    replace.append((key, value))

        for key, value in replace:
            dic[key].remove(value)
            addvalue = []
            for letter in value:
                addvalue.append(dic[letter])
            for item in list(product(*addvalue)):
                dic[key].append(''.join(item))
    Vn, Vt = refreshVnVt(dic)
    Vn, Vt = checkNeed(dic, Vn, 0)
    printResult(checkNtoN.__name__, [dic, Vn, Vt])
    return Vn, Vt


def checkLeftRecur(dic, Vn):
    Vn, Vt = refreshVnVt(dic)
    printResult(checkLeftRecur.__name__, [dic, Vn, Vt])
    return Vn, Vt


def checkLeftFactor(dic, Vn):
    Vn, Vt = refreshVnVt(dic)
    printResult(checkLeftFactor.__name__, [dic, Vn, Vt])
    return Vn, Vt

def printResult(funcName, arr):
    print()
    print(f"func {funcName}")
    p = ["dic", "Vn", "Vt"]
    for idx, val in enumerate(arr):
        print(f"{p[idx]} = {val}")

P1 = {"S->aB", "A->aB|aC", "B->C|Ee|Bb", "C->b", "D->c", "E->fEgE|gEfE|ε"}
P2 = {"S->ABCD", "A->B|a", "B->C|b", "C->c|ε", "D->Dd"}
P3 = {"S->A|B", "A->C", "C->c", "B->ED|b", "E->e|f", "D->d"}

for P in [P1, P2, P3]:
    print("---------------")
    print(P)
    dic, Vn, Vt = initialization(P)
    Vn, Vt = checkNeed(dic, Vn)
    Vn, Vt = checkNull(dic, Vn, Vt)
    Vn, Vt = checkNtoN(dic)
    Vn, Vt = checkLeftFactor(dic, Vn)
    Vn, Vt = checkLeftRecur(dic, Vn)
    print("---------------")

Last updated

Was this helpful?