Py) 전처리 - 금액 처리

Py) 전처리 - 금액 처리

데이터를 전처리하다 보면 금액 데이터를 처리해야 하는 경우가 종종 있다. 이를 어떻게 효율적으로 처리하는지 알아보고자 한다.


이론

데이터에서 금액의 표기는 다양하다. 1000원 100$ 같이 뒤에 통화 단위가 붙을 수도 있고, 1000 같이 숫자만 표기되는 경우도 있다. 그리고 자리수 구분을 위해 쉼표(,)를 사용하는 경우도 있다. 이런 데이터를 처리하기 위해 정규표현식을 사용하는 것이 효율적이다.

그리고 특정 나라에서는 소수점 표기 시 온점을 사용하지 않고 쉼표를 사용하는 경우도 있다. 예를 들어 123.45123,45로 표기하는 경우이다. 아래는 예전에 스위스에서 직접 찍은 사진인데 경악을 금치 못했던 기억이 있다.
Swiss에서 직접 찍은 소수점 표기

이 경우에는 쉼표를 제거하고 온점으로 변경해주어야 하는데 (작성중)

실습

다음과 같이 라이브러리와 데이터를 준비한다.

1
2
3
4
import numpy as np
import pandas as pd

ser1 = pd.Series(["3,000원", "Price: $400", "-50012.5", "2,345.67", "1,234", "Invalid"])

기본 처리

우선 숫자를 제외한 나머지를 제거해보자.

1
2
3
4
5
6
7
8
ser1.str.replace(pat = "[^0-9]", repl = "", regex = True)
## 0 3000
## 1 400
## 2 500125
## 3 234567
## 4 1234
## 5
## dtype: object

음수, 쉼표, 온점 등등 다 제거가 되긴 하지만 원래 수치 값을 제대로 살리진 못하는 문제점이 있다. 그래서 이를 보완하기 위해 다음과 같이 정규식을 수정해보자.

1
2
3
4
5
6
7
8
ser1.str.replace(pat = "[^0-9.]", repl = "", regex = True)
## 0 3000
## 1 400
## 2 50012.5
## 3 2345.67
## 4 1234
## 5
## dtype: object

여기에서 사용한 온점(마침표)은 정규식의 대괄호 내부에 들어있기 때문에 모든 문자열을 뜻하는 것이 아니라 온전히 plain text로 인식되며 온점 그대로를 뜻한다. 만약 상기 내용이 이해하기 어렵다면 다음의 예제로 어떻게 정규식이 동작하는지 확인해보자.

1
2
3
4
5
6
7
8
9
10
ser_test = pd.Series(["1.2", "123.4"])
ser_test.str.replace(pat = "[.]", repl = "", regex = True)
## 0 12
## 1 1234
## dtype: object

ser_test.str.replace(pat = "\.", repl = "", regex = True)
## 0 12
## 1 1234
## dtype: object

아무튼 “[^0-9.]“로도 해결이 안된 것이 있는데 바로 음수이다. 이를 해결하기 위해 기존 정규식을 다음과 같이 수정해보자.

1
2
3
4
5
6
7
8
ser1.str.replace(pat = "[^0-9.-]", repl = "", regex = True)
## 0 3000
## 1 400
## 2 -50012.5
## 3 2345.67
## 4 1234
## 5
## dtype: object

환율 처리

여기서 끝난 것은 아니다. 기존에 “$400”인 것이 있었기 때문에 기존 데이터의 통화 단위가 “원”이고 달러 기호가 있는 원소의 경우 이를 환률 기준으로 반영하고자 한다면 별도로 처리를 해주어야 한다. 다음의 코드를 통해 이를 처리해보자.

1
2
3
df = ser1.str.replace(pat = "[^0-9.$-]", repl = "", regex = True).to_frame()
df.columns = ["m"]
df
m
0 3000
1 $400
2 -50012.5
3 2345.67
4 1234
5

상기 코드 초반에 정규식을 “[^0-9.$-]“ 로 사용했는데 만약 달러 기호를 처리하기 위해 정규식을 “[^0-9.-$]“으로 작성한다면 에러가 발생한다. 이는 정규식의 대괄호 내부에 하이픈이 범위로 동작하기 때문이다. 그래서 대괄호 내부에 연속범위가 아닌 단일 문자 매칭을 위해 하이픈을 사용해야 한다면 되도록이면 정규식 가장 앞이나 뒤에 배치하는 것을 권장한다.

이제 달러를 환율에 맞게 처리하기 위해 신규 변수 “is_dollar”를 만들고 기존 변수 “m”에서 달러 기호를 제거한 변수 “m2”를 만들어보자.

1
2
3
df["is_dollar"] = np.where(df["m"].str.contains("^\$"), 1, 0)
df["m2"] = df["m"].str.replace(pat = "$", repl = "")
df
m is_dollar m2
0 3000 0 3000
1 $400 1 400
2 -50012.5 0 -50012.5
3 2345.67 0 2345.67
4 1234 0 1234
5 0

여기서 굳이 신규 변수를 만들지 않고 한번에 처리할 수 있긴 하지만 이해를 돕기 위해 신규 변수를 만들어보았다. 이제 달러 기호가 있는 경우에만 환율을 적용해보자.

1
2
3
df["m3"] = df["m2"].replace("", 0).astype("float")
df["m4"] = np.where(df["is_dollar"] == 1, df["m3"] * 1400, df["m3"])
df
m is_dollar m2 m3 m4
0 3000 0 3000 3000.00 3000.00
1 $400 1 400 400.00 560000.00
2 -50012.5 0 -50012.5 -50012.50 -50012.50
3 2345.67 0 2345.67 2345.67 2345.67
4 1234 0 1234 1234.00 1234.00
5 0 0.00 0.00

문자 데이터에 수치연산을 하기 위해 .replace("", 0).astype("float") 를 사용했는데 .replace("", 0)를 사용하지 않고 그냥 바로 .astype("float")를 사용하게 되면 빈칸을 처리할 수 없어 에러가 발생하기 때문에 .replace("", 0)를 추가한 코드를 사용한 것이다.

아무튼 np.where() 함수를 통해 기존에 달러 기호가 적혀있던 원소에만 환율을 곱할 수 있었다.

쉼표 처리

사실상 소수점 표기를 위해서 온점 대신 쉼표를 사용하는 것과 자리수 구분을 위해 세 자리 마다 쉼표를 사용하는 경우가 동시에 반영된 데이터라면 매우 곤란하긴 하다. 소수점 세자리까지 적혀있는 경우라면 별도의 값 범위 제한 등 조건이 붙지 않는 한 해당 쉼표가 소수점 표기로 사용된 쉼표인지 자리수 구분을 위해 사용된 쉼표인지 알기 어렵다. 그래서 곤란한 경우를 제외하고 비교적 간단한 경우에 대해서만 다루어보자.

예를 들어 다음과 같이 소수점 표기를 위해서만 쉼표가 사용되었다면 간단하게 처리를 할 수 있겠다.

1
2
3
4
5
6
7
8
9
10
11
12
ser2 = pd.Series(["123,4", "12435,7", "-342,25"])
ser2
## 0 123,4
## 1 12435,7
## 2 -342,25
## dtype: object

ser2.str.replace(pat = ",", repl = ".").astype("float")
## 0 123.40
## 1 12435.70
## 2 -342.25
## dtype: float64

하지만 다음과 같은 경우 조금 더 복잡해진다.

1
2
3
4
5
6
7
8
ser3 = pd.Series(["5,123,9", "23,4", "123,234", "3,623,234,43", "765"])
ser3
## 0 5,123,9
## 1 23,4
## 2 123,234
## 3 3,623,234,43
## 4 765
## dtype: object

여기서는 마지막 쉼표를 온점으로 바꿔야 할 경우에 다음과 같이 작성할 수 있다.

1
2
3
4
5
6
7
8
ser3_2 = ser3.str.replace(pat = ",(?![0-9]*,)", repl = ".", regex = True)
ser3_2
## 0 5,123.9
## 1 23.4
## 2 123.234
## 3 3,623,234.43
## 4 765
## dtype: object

상기 코드에서 사용한 정규식은 부정형 전방탐색으로 소괄호 내부의 물음표(?)가 전방탐색을 의미하고 느낌표(!)가 부정을 의미한다. 즉, 소괄호 내부의 “?!” 뒤에 나오는 패턴이 존재하지 않아야(부정) 한다는 의미이다. 그래서 최종적으로 쉼표 뒤에 숫자 개수에 관계없이 또 마지막에 쉼표가 위치하지 않아야 하는 패턴([0-9]*,)이 없는 경우만 처리된다. 즉, 마지막 쉼표만 온점으로 바뀌게 된다.

이제 필요한 쉼표가 온점으로 바뀌었으니 남아있는 쉼표를 제거하면 다음과 같다.

1
2
3
4
5
6
7
ser3_2.str.replace(",", "")
## 0 5123.9
## 1 23.4
## 2 123.234
## 3 3623234.43
## 4 765
## dtype: object
Your browser is out-of-date!

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

×