Py) 전처리 - 카드 게임의 본선 진출자는 누구?

Py) 전처리 - 카드 게임의 본선 진출자는 누구?

플레이어가 뽑은 카드와 대진표 정보를 매칭하여 어떤 플레이어가 예선을 통과하여 본선 경기로 진출하는지 알아보자.

두 데이터프레임을 매칭하여 상위 N 번째 값을 가지는 원소를 추출하는 예제를 카드게임의 예선전에 대입하여 알아보자.

문제 상황

다음과 같은 데이터를 정제하여 결과를 도출해야 한다. 먼저 각 대회 참가자가 뽑은 카드의 번호이다.

1
2
3
4
import pandas as pd
df1 = pd.DataFrame([["A", 1], ["C", 2], ["B", 3], ["F", 4], ["D", 5]],
columns = ["Rank", "Score"])
df1
Rank Score
0 A 1
1 C 2
2 B 3
3 F 4
4 D 5

각 대회 참가자의 대진표는 임의의 확률과정에 의해 이루어졌다고 가정하고. 해당 결과는 다음에서 확인할 수 있다.

1
2
3
df2 = pd.DataFrame([["A,C,D,F"], ["B,F,D"], ["C,A,B"]],
columns = ["Match"])
df2
Match
0 A,C,D,F
1 B,F,D
2 C,A,B

대진표에 따라 각 참가자간 카드 숫자를 비교하여 본선으로 진출하는 상위 2명의 정보가 있는 정보는 다음과 같다.

1
2
3
df_result = pd.DataFrame([["C,A"], ["F,B"], ["C,A"]],
columns = ["Winner"])
df_result
Winner
0 C,A
1 F,B
2 C,A

과정

우선 대진표 객체 “df2”의 각 원소를 쪼개어보자.

1
2
3
df2.iloc[0, :].str.split(pat = ",")
## Match [A, C, D, F]
## Name: 0, dtype: object

pd.Series.str.split() 을 활용하여 각 텍스트를 쉼표 기준으로 쪼개면 각 알파벳이 원소인 객체를 얻을 수 있다. 이제 이 객체를 기반으로 참가자의 점수가 있는 “df1” 정보와 매칭을 해야 하는데 이를 위해서는 반복문을 활용한 접근법과 join 연산을 활용한 접근법이 있다. 그런데 반복문을 활용한 접근법은 연산 효율이 좋지 않고, join 연산의 경우 데이터프레임간 연산이 필요하니 보다 간단히 하기 위해서 Pandas 의 Series로 접근하고자 한다.

“Rank” 변수를 .set_index() 메서드로 인덱스 지정을 한 후에 .iloc[] 인덱서를 사용하면 다음과 같이 데이터프레임이 아닌 Series를 뽑을 수 있다.

1
2
3
4
5
6
7
8
9
ser = df1.set_index("Rank").iloc[:, 0]
ser
## Rank
## A 1
## C 2
## B 3
## F 4
## D 5
## Name: Score, dtype: int64

그리고 해당 객체를 index 기반 연산을 하여 매칭되는 원소를 뽑을 수 있다.

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
ser_sub = df2.iloc[0, :].str.split(pat = ",")[0] # 1
ser_sub
## ['A', 'C', 'D', 'F']

ser[ser.index.isin(ser_sub)] # 2
## Rank
## A 1
## C 2
## F 4
## D 5
## Name: Score, dtype: int64

ser[ser.index.isin(ser_sub)].sort_values(ascending = False) # 3
## Rank
## D 5
## F 4
## C 2
## A 1
## Name: Score, dtype: int64

ser[ser.index.isin(ser_sub)].sort_values(ascending = False)[:2] # 4
## Rank
## D 5
## F 4
## Name: Score, dtype: int64

",".join(ser[ser.index.isin(ser_sub)].sort_values(ascending = False)[:2].index.values) # 5
## 'D,F'

- 1번 코드: 원소가 문자인 리스트를 뽑기 위해 [0] 를 사용해야 하니 주의하자.
- 2번 코드: 인덱스와 입력된 값을 매칭하기 위해 .isin() 을 사용하였다.
- 3번 코드: 점수가 높은 순서대로 정렬하기 위해 .sort_values() 를 사용하였다.
- 4번 코드: 상위 2명을 뽑기 위해 3번 코드의 결과에 [:2] 를 추가하였다.
- 5번 코드: 결과를 하나의 변수로 이어붙이기 위해 .join() 메서드를 사용하였다.

참고로 5번의 경우는 pandas.Series.str.cat() 을 사용할 수 없는 “numpy.ndarray”를 다뤄야 하기 때문에 .join() 을 사용하였다.

결과

결과는 다음과 같으며 필요시 앞에서 제시한 결과물 처럼 데이터프레임으로 변경할 수 있다.

1
2
3
4
5
df2.apply(lambda x: ",".join(ser[ser.index.isin(x.str.split(pat = ",")[0])].index.sort_values()[:2]), axis = 1)
## 0 A/C
## 1 B/D
## 2 A/B
## dtype: object

여기서 사용한 lambda 함수를 사용자 정의 함수로 바꿀 경우 입/출력 데이터를 쪼개고 붙이는 구분자 관련 설정과 본선에 올라갈 상위 n명을 설정하는 인자를 넣을 수 있겠다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×