Excel 수식이 너무 복잡하고 들여 쓰기를 원했을 때 한 일

13051 단어 파이썬ExcelOpenpyxl

개요



Excel의 수식란에 상당한 길이의 수식이 묻혀 있어, 읽어내기에도 수정하는데도 상당한 고생이 필요하게 되어 버렸기 때문에, 일단 빼내 해 해석을 시도한 이야기.

데이터 추출에는 Openpyxl을 사용했습니다.

이런 상황이었





Excel의 수식에 넣어 좋은 문자열은 75문자까지라든지 누군가 결정해 줘・・・w
(계산 결과의 도중을 어느 쪽의 셀에 내뿜는다든가···.)

특히 Excel의 IF는 읽기가 어렵습니다.


=IF(A1="これこれ", "xxxx", IF(B1="あれこれ", "yyyy", "zzzz"))

이런 쓰는 방법, 잘 하는군요・・・.
if (A1 === "これこれ") {
   cell = "xxxx";
} elseif (B1 === "あれこれ") {
   cell = "yyyy";
} else {
   cell = "zzzz";
}

라는 느낌입니다만, 독자 함수 정의하면 매크로가・・・라고 이야기가 되고, 리팩토링도 어렵습니다. 그래서 들여쓰거나 해서 어떻게든 가독성을 줄 수밖에 없네요.

덧붙여서 Excel 수식에 있어서의 들여쓰기에, N() 함수를 더해 코멘트같이 쓸 수 있거나 배열 수식을 사용해 코멘트같이 쓰는 방법도 있는 것 같습니다・・・

→ 수식 작성에 유용한 기법

이번에는 값을 꺼내 ~ 들여 쓰기를 추가하는 곳을 시도합니다.



물론 매크로를 사용하여 해결하는 방법도 있네요.
→ Excel에서 중첩된 If 함수를 VBA로 들여쓰고 분석하기 쉽습니다.

파이썬으로 시도



이번에는 Openpyxl을 사용해 보겠습니다.

드디어, 수식의 해석은 Parsing Formula (을)를 사용해 할 수 있는 것 같습니다.

우선 써 있는 것을 시도해 봅시다.
>>> from openpyxl.formula import Tokenizer
>>> tok = Tokenizer("""=IF(A1="これこれ", "xxxx", IF(B1="あれこれ", "yyyy", "zzzz"))""")
>>> print("\n".join("%12s%11s%9s" % (t.value, t.type, t.subtype) for t in tok.items))
         IF(       FUNC     OPEN
          A1    OPERAND    RANGE
           =OPERATOR-INFIX
      "これこれ"    OPERAND     TEXT
           ,        SEP      ARG
            WHITE-SPACE
      "xxxx"    OPERAND     TEXT
           ,        SEP      ARG
            WHITE-SPACE
         IF(       FUNC     OPEN
          B1    OPERAND    RANGE
           =OPERATOR-INFIX
      "あれこれ"    OPERAND     TEXT
           ,        SEP      ARG
            WHITE-SPACE
      "yyyy"    OPERAND     TEXT
           ,        SEP      ARG
            WHITE-SPACE
      "zzzz"    OPERAND     TEXT
           )       FUNC    CLOSE
           )       FUNC    CLOSE

과연. 선두로부터 각각의 파트가 함수(시작·종료)인지, 변수인지 등등으로 분해해 주는 것 같다.

라고 하는 것은 함수가 개시하면 개행해, 다음의 행에는 들여쓰기를 붙여, 함수가 종료할 때에 들여쓰기를 줄이고 나서 개행해 가면 된다고 하는 것이군요.

이해하기 쉽도록 들여 쓰기를 시각화하고 씁니다.


from openpyxl.formula import Tokenizer
# あとでindent_charはもちろんスペースに直すよ
def parse(formula, indent_char = "_"):
    # 最終的に文字列として結合して返す
    result = ""
    tok = Tokenizer(formula)
    # インデント数
    n = 0
    # 直前で処理したトークン
    last = None

    for t in tok.items:
        if t.type == "FUNC":
            if t.subtype == "OPEN":
                if last is not None and (last.type == "FUNC" and last.subtype == "OPEN"):
                    n += 2
                    result += indent_char * n + t.value
                else:
                    result += t.value

                result += "\n"
            else:
                # function close。インデント数を戻す
                n -= 2
                result += "\n" + indent_char * n + t.value

        else:
            # それ以外は単純結合。直前が関数の場合はインデント
            if last is not None and (last.type == "FUNC" and last.subtype == "OPEN"):
                n += 2
                result += indent_char * n + t.value
            else:
                result += t.value

        last = t

    return result

s = """=IF(A1="これこれ", "xxxx", IF(B1="あれこれ", "yyyy", "zzzz"))"""

print(parse(s))
IF(
__A1="これこれ", "xxxx", IF(
____B1="あれこれ", "yyyy", "zzzz"
__)
)

그래. 이것으로 좋을 것 같다.

나머지는 Excel의 지정된 셀 범위를 전달합니다.


wb = load_workbook(filename, data_only = False)
sheet_name = "Sheet1"
cell_range = "A2:C2"
for line in wb[sheet_name][cell_range]:
    for cell in line:
        # formula のみを抽出
        if cell.data_type == "f":
            f = parse(cell.value)
            # cell.coordinateには「A1」などの表現が入る
            with open(cell.coordinate + ".txt", mode='w') as fl:
                fl.write(f)

이런 식으로 하면 수식이 묻혀 있는 셀만 A2.txt, C2.txt처럼 들여쓰기된 텍스트로 내뿜을 수 있습니다.

좋은 웹페이지 즐겨찾기