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처럼 들여쓰기된 텍스트로 내뿜을 수 있습니다.

좋은 웹페이지 즐겨찾기