🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

5536閲覧

階層構造テキストを可視化する方法を探しています

hhiro45fd

総合スコア19

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2021/01/26 08:22

編集2021/01/26 08:49

前提・実現したいこと

【前提】
階層構造のテキストファイルがあります(以下の例参照)
・例(A~Gのテキスト。「.」が階層レベルを示す。ファイル名「test.txt」とする。テキストは100個ほど存在。)
.A
..B
.C
..D
..E
...F
.G

【実現したいこと】
このテキストを以下のような階層構造としてpythonを用いて可視化したいです。
・最終出力はテキストでなくてもかまいません。(png等でもOK)
以下のような垂れ下がり型(?)の形状を希望(扇型に広がったいるのはNG)

test

├─ A
│ │
│ └─ B

├─ C
│ │
│ ├─ D
│ │
│ └─ E
│ │
│ └─ F

└─ G

発生している問題・エラーメッセージ

・扇形に書けるものはgraphvizなど色々見つかったのですが、
垂れ下がり型で書ける方法が分かりません。
・唯一以下のURLに垂れ下がり型でテキスト出力の物があったのですが、
元テキスト(test.txt)から階層構造を自動出力する方法が構築できず途方に暮れています。
参考URL:https://rosettacode.org/wiki/Visualize_a_tree#Python

該当のソースコード(参考URLより拝借)

python

1'''Visualize a tree''' 2 3from itertools import (chain, repeat, starmap) 4from operator import (add) 5 6 7'''drawTree :: Tree a -> String''' 8def drawTree(tree): 9 '''ASCII diagram of a tree.''' 10 return '\n'.join(draw(tree)) 11 12 13'''draw :: Tree a -> [String]''' 14def draw(node): 15 '''List of the lines of an ASCII 16 diagram of a tree.''' 17 def shift(first, other, xs): 18 return list(starmap( 19 add, 20 zip( 21 chain([first], repeat(other, len(xs) - 1)), 22 xs 23 ) 24 )) 25 26 def drawSubTrees(xs): 27 return ( 28 ( 29 ['│'] + shift( 30 '├─ ', '│ ', draw(xs[0]) 31 ) + drawSubTrees(xs[1:]) 32 ) if 1 < len(xs) else ['│'] + shift( 33 '└─ ', ' ', draw(xs[0]) 34 ) 35 ) if xs else [] 36 37 return (str(root(node))).splitlines() + ( 38 drawSubTrees(nest(node)) 39 ) 40 41 42''' TEST ----------------------------------------------------''' 43'''main :: IO ()''' 44def main(): 45 '''Test''' 46 47 '''tree :: Tree Int''' 48 tree = Node(1)([ 49 Node(2)([ 50 Node(4)([ 51 Node(7)([]) 52 ]), 53 Node(5)([]) 54 ]), 55 Node(3)([ 56 Node(6)([ 57 Node(8)([]), 58 Node(9)([]) 59 ]) 60 ]) 61 ]) 62 63 print(drawTree(tree)) 64 65 66''' GENERIC -------------------------------------------------''' 67 68 69''' Node :: a -> [Tree a] -> Tree a''' 70def Node(v): 71 '''Contructor for a Tree node which connects a 72 value of some kind to a list of zero or 73 more child trees.''' 74 return lambda xs: {'root': v, 'nest': xs} 75 76 77''' nest :: Tree a -> [Tree a]''' 78def nest(tree): 79 '''Accessor function for children of tree node.''' 80 return tree['nest'] if 'nest' in tree else None 81 82 83''' root :: Dict -> a''' 84def root(dct): 85 '''Accessor function for data of tree node.''' 86 return dct['root'] if 'root' in dct else None 87 88 89''' MAIN ---''' 90if __name__ == '__main__': 91 main()``` 92

試したこと

上記のコードを用いて手作業でNodeを構築すれば、階層構造を作成できたのですが、
元テキストから自動で階層構造を構築する方法が全く分かりません。

補足情報(FW/ツールのバージョンなど)

python3.7
anaconda 環境

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hhiro45fd

2021/01/26 08:50

すいません、失念してました。修正いたしました。
YND_teratail

2021/01/26 08:53

ありがとうございます。
guest

回答3

0

ベストアンサー

ライブラリ anytree を使った方法

python

1import fileinput 2from anytree import Node, RenderTree # 要: pip install anytree 3 4# ルートノードのみ準備 5root = Node("test") 6stack = [root] 7 8for line in fileinput.input(): # 標準入力 or 引数のファイルから読込 9 10 # 入力データのparse 11 # ※ ここは実際のデータに合わせて要調整 (depth: 階層の深さ、value: 表示する値) 12 # 例: "...C\n" -> depth:3, value:"C" 13 depth = line.count(".") # ドットの数をノードの深さ 14 value = line.lstrip(".").rstrip() # ドットを取り除いた部分を値 15 assert "." not in value 16 17 # 木構造データの構築 18 while len(stack) > depth: 19 stack.pop() 20 node = Node(value, parent=stack[-1]) 21 stack.append(node) 22 23# テキスト出力: 以下は anytree ライブラリのサンプルコードそのまま 24for pre, fill, node in RenderTree(root): 25 print("%s%s" % (pre, node.name))

importer/exporter で複数のデータの読み込みと書き出しの方法を選択できます。

anytree ライブラリのブランチに indented importer というものがあったそうです。
最近の情報を見ると 3日前に PR が投稿されてました。
https://github.com/c0fec0de/anytree/pull/155

過去のバージョンで使えていて、保守から外れた経緯があるようなので、
PRの実装が採用されるかどうかは解りませんが、
コード自体はドットをスペースに変換すればそのまま使えそう。


質問に掲載のコードで改善可能な点

ソースコード中のコメント部分から Haskell っぽい所があったので言及しておくと

drawTree関数の実装
print("\n".join(...)) では、全ての出力結果のリストを作ってから
改行文字で連結し、一度に出力という処理順序になる為、
データ数が多いと、最初に何も表示されないまま少し時間が掛かかります。

PythonでHaskellのような遅延評価をするには、for文を使い、リストの扱いを
イテレーターやジェネレーターにすることで、一行ずつ出力が可能です。

投稿2021/01/27 09:43

teamikl

総合スコア8738

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hhiro45fd

2021/01/29 03:46

ご回答ありがとうございます。 非常に詳細にありがとうございます。あっさりとできてしまいました。 anytree・・・不勉強なため初耳でした。 補足の部分も併せてじっくり読ませて頂きます。
guest

0

ちゃんと表示されるかわかりませんが、少なくともサンプルの場合はできます

py

1import re 2pRt="├─" 3pLRt="└─" 4pTt="│" 5pSp=" " 6filename="File1" 7file = open(filename, "r") 8lines = file.readlines() 9file.close() 10nextDotNum=0 11tree="" 12nextRt=100 13for line in reversed(lines): 14 dotNum=len(re.search(r"^.+",line).group()) 15 str=re.search(r"(?<=.)[^.]+$",line).group().strip() 16 brc="" 17 if(dotNum>1): 18 brc=pTt 19 for i in range(dotNum-1): 20 brc+=pSp 21 if(dotNum==nextDotNum): 22 pR=pRt 23 else: 24 print(str) 25 if(dotNum>nextDotNum): 26 pR=pLRt 27 elif(dotNum==nextDotNum): 28 pR=pRt 29 elif(dotNum>nextRt): 30 pR=pLRt 31 else: 32 pR=pRt 33 brc="\n"+brc+pTt+"\n"+brc+pR+" "+str 34 tree=brc+tree 35 nextDotNum=dotNum 36 if(dotNum>nextDotNum): 37 nextRt=nextDotNum 38 if(dotNum<nextRt): 39 nextRt=dotNum 40tree=filename+tree 41print(tree)

投稿2021/01/26 18:22

編集2021/01/26 18:25
modieu

総合スコア282

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hhiro45fd

2021/01/29 03:39

ご回答ありがとうございます。 いくつかテストしてみましたが、今の所問題はなさそうです。 考え方が非常に参考になりました。ありがとうございます。
guest

0

半分冗談ですが頑張って作りました。

$ cat hoge.py #!/usr/bin/env python3 buf=""" .A ..B .C ..D ..E ...F .G """ parent=['.','.','.','.','.'] for line in buf.split("\n"): i=0 for c in list(line): if c == '.': print(parent[i]+"/", end='') else: print(c+"/", end='') parent[i]=c i+=1 print("") $ ./hoge.py ./A/ ./A/B/ ./C/ ./C/D/ ./C/E/ ./C/E/F/ ./G/ $ mkdir -p `./hoge.py` $ tree . ├── A │ └── B ├── C │ ├── D │ └── E │ └── F └── G 7 directories, 3 files

投稿2021/01/26 11:37

68user

総合スコア2022

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

68user

2021/01/26 11:42 編集

標準入力から読めるんですね…。 $ (echo ほげ; echo ほげ/ふが; echo あいう; echo ほげ/ううう) |tree --fromfile=. . ├── あいう └── ほげ   ├── ううう   └── ふが
hhiro45fd

2021/01/29 02:50

ご回答ありがとうございます。 なんという荒技! 真面目にやったら名称に結構制約が厳しいようで残念ながら諦めました・・・ (大変面白かったです、ありがとうございます<(_ _)>)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問