본문 바로가기
Working on

[python] tabulate 에서 float , int , bool type 출력 문제 해결방법

by Warehaus 2021. 11. 27.

프로젝트에서  table 을 구성하여 출력하기 위해 python 의 tabulate module을 계속해서 사용하고 있었다.

 

그런데 문제가 발생했는데,  그건 바로 type이 섞여있는 경우에 원하는 값을 정상적으로 출력 해 주지 못하는 점이다.

 

아래는 내가 겪었던 문제에 대해서 간단히 테스트코드를 통해 보여준다.

 

일단 일반적인 값을 출력하는데는 전혀 문제가 없다.

data = [(1,2),(3,4)]

print(data)
columns = ['col1', 'col2' ]

print("\n")
print(tabulate(data, headers=columns))

# 출력
[(1, 2), (3, 4)]


  col1    col2
------  ------
     1       2
     3       4

 

여기에서 데이터를 조금 섞어보자. 그리고 floatfmt 옵션을 이용해 소숫점을 2자리로 제한한다.

# set mixed type
print("\n")
data = [(1,2),(33,"Hello"), (3.1234,"3.23")]
print(tabulate(data, headers=columns))

# set mixed type with float format 1
print("\n")
data = [(1,2),(33,"Hello"), (3.1234,"3.23"), ("2.3","3.23")]
print(tabulate(data, headers=columns, floatfmt=".2f"))

# 출력
   col1  col2
-------  ------
 1       2
33       Hello
 3.1234  3.23


  col1  col2
------  ------
  1.00  2
 33.00  Hello
  3.12  3.23
  2.30  3.23

 

조금 이상한 점이 보였는가? 

1, 33 은 integer임에도 불구하고 1.00으로 소숫점 처리가 되어버렸다.

 

이런 결과를 사용자에게 보여주면, 사용자는 뭔가 지저분하다고 생각할 것이다.

# set mixed type with float format 2
print("\n")
data = [(False, True),(33,"Hello"), (3.1234,"3.23") , ("3.13242","3.23")]
print(tabulate(data, headers=columns))

# 출력

    col1  col2
--------  ------
 0        True
33        Hello
 3.1234   3.23
 3.13242  3.23

 

Bool값을 출력하는데도 문제다.

False 를 출력하고 싶었는데 0으로 나와버린다.

 

어떻게든 해보려고 했지만, 결론은 유사한 코드를 개발하는 수 밖에 없었다.

 

뭐 사실 넘어오는 값들을 하나씩 변경해서 string으로  tabulate에 넣는 것도 방법이라면 방법일 수 있겠다 싶었다.

과연 그럴까?

# set mixed type with float format 3
print("\n")
data = [("False", "True"),("33","Hello"), ("3.1234","3.23") , ("3.13242","3.23")]
print(tabulate(data, headers=columns))

#출력

Traceback (most recent call last):
  File "211127.tabulate.issue.py", line 30, in <module>
    print(tabulate(data, headers=columns))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/tabulate.py", line 1593, in tabulate
    for c, ct, fl_fmt, miss_v in zip(cols, coltypes, float_formats, missing_vals)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/tabulate.py", line 1593, in <listcomp>
    for c, ct, fl_fmt, miss_v in zip(cols, coltypes, float_formats, missing_vals)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/tabulate.py", line 1592, in <listcomp>
    [_format(v, ct, fl_fmt, miss_v, has_invisible) for v in c]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/tabulate.py", line 996, in _format
    return format(float(val), floatfmt)
ValueError: could not convert string to float: 'False'

 

진짜 귀찮게 포맷체크는 왜하는지 모르겠다.

아무튼 결론적으로 string을 그냥 그대로 출력해 주는 tabulate 을 만들기로 결정했다.

 

아래와 같은 절차로 개발을 수행했다.

 

1. tabulate code review

사실 기존 tabulate code에 참고할 만한 내용이 상당히 많다.

row, column  계산을 통한 너비조정 및 padding 설정하는 코드를 확인하고 필요한 부분을 정리했다.

 

우리는 별도로 정렬하는 코드는 필요하지 않았고, value 부분을 타입을 확인하지 않고 전달되는 string  그대로 출력해 줄 수 있는 기능만 필요했다.

 

2. custom tabulate

 

이 단계에서는 새로운 모듈을 작성했다.

a_tabulate.py 라 이름짓고  (실제와는 다르지만), 이를 구현하기 시작했다.

 

물론 내용은 tabulate 와 꽤 차이는 있었지만, 기본적인 table 구성방식은 유사했다고 봐야했다.

그래서 라이선스 걱정이 되기 시작했다.

 

3. Put the license in code

 

다행히도 tabulate license는 상업적 이용이 가능한  라이선스다. ( MIT license )

하지만 이를 참고하는 코드 내에 라이선스 표시를 해 줘야하는 제약이 있다. 

 

사실 이걸 제약이라고 말하기도 어렵기는 하지만.. 뭐 아무튼 사용하기 위해서는 라이선스를 명시해 줘야 한다.

 

SW라이선스 표시하는 행위에 그렇게 익숙하지 않다보니, 어떻게 써야할 지 조금은 막막했다.

우리는 모든 코드 상단에 회사 라이선스임을 명시하고 있었는데, 이를 빼고  MIT라이선스를 넣어야 하는건지.. 별도로 README 를 만들어 둬야하는 것인지 조금 애매했다.

 

Tachnical leader 에게 확인해 보니 그냥 라이선스 두개 다 써버려도 크게 문제되지 않는다고 하여 연달아 입력 해 버렸다.

어느정도 라이선스 조약을 준수하려는 effort가 있었다는 정도로도 괜찮은 것 같았다. ( MIT라이선스 자체가 그렇게 빡빡하지가 않다 )

 

아래 예시처럼 입력 해 줬다. MIT 라이선스를 선 기입하고, 회사의 카피라이트를 기입 하였다.

#MIT License

#Copyright (c) 2020 OpenAI

#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:

#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.

#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.

# Copyright My company..
#....

 

4. check-in

 

코드 구현을 완료하고 체크인!

Pull request 에서 사소한 피드백을 반영 후 개발 branch에 merge 를 완료했다.

 

 

다소 조잡한 포스팅이라 생각하지만 MIT 라이선스를 사용하는 Python module을 일부 고쳐서 사용하고자 하는 사람들에게 도움이 되었으면 한다. 혹은, tabulate의 요상한 동작으로 고생(?)을 하고있는 사람이 혹시나 있다면 그냥 ... 새로 만드는걸 권하고 싶어서 짧게나마 작성 해 보았다.

 

사실 이 부분을 github에서 개선사항으로 report할까 싶기도 하다.

분명히 module 사용성을 개선하는 데 있어 도움이 될 것 이라 생각이 된다.