4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python3で複数の配列を同時にソート

Last updated at Posted at 2018-08-22

やりたいこと

Python3で複数の配列を同時にソートしたい!
...とは言っても、うまく伝わらないと思うので、具体例を挙げます。

対応関係

下記の対応(<->)は、辞書のようなもの。
ex.「私」を数字で表現すると「0」、「1」に対応する言葉は「は」

0 <-> 私
1 <-> は
2 <-> 学生
3 <-> です
4 <-> 。
5 <-> 宇宙
6 <-> が
7 <-> 好き
8 <-> でした

この辞書を使って、文章を数字列に変換(数字列を文章に変換)すると、

私 は 学生 です 。        <-> [0 1 2 3 4]
私 は 学生 でした 。      <-> [0 1 2 8 4]
私 は 宇宙 が 好き です 。 <-> [0 1 5 6 7 3 4]

ソート前のデータ

  • 上記の辞書変換によって、文章と数字列が対応。
  • 1行の数字列に対して、1つのラベルが割り当てられている。(1対1に対応しているとは限らない)

    ex. 1行目の[0 1 2 3 4] <-> a

    ex. 4行目の[0 1 2 3 4] <-> d
並び順 数字列 文章 ラベル
1 [0 1 2 3 4] 私は学生です。 a
2 [0 1 5 6 7 3 4] 私は宇宙が好きです。 b
3 [0 1 2 8 4] 私は学生でした。   c
4 [0 1 2 3 4] 私は学生です。 d

ソート後のデータ

  • 数字列と文章、ラベルの対応関係が保たれている
  • 重複した数字列が連続で出てくる
  • 数字列の「1つ目の要素」でソート → 「2つ目の要素」でソート → ... → 「5つ目の要素」でソート
並び順 数字列 文章 ラベル
1 [0 1 2 3 4] 私は学生です。 a
2 [0 1 2 3 4] 私は学生です。 d
3 [0 1 2 8 4] 私は学生でした。   c
4 [0 1 5 6 7 3 4] 私は宇宙が好きです。 b

実行環境

  • macOS High Sierra (ver: 10.13.5)
  • Anaconda (ver: 4.5.6)
  • Python 3.6.5 :: Anaconda, Inc.
  • Python対話モード

参考記事に沿ってやってみた

複数の配列(ここでは数字列とラベル)の関係性を保ったまま、ソートができないか調べた。
...この記事がドンピシャっぽいので実行してみた。

対話モード開始

$ python

データの作成

>>> a = []; b = []; c = [] # 初期化(* c = []まで入力した後、エンターキーを押すと、次の入力を受け付けることを示す「>>>」が出てくる)

>>> a.append(list(range(5))) # a.append([0,1,2,3,4])と同じこと
>>> a.append([0 1 5 6 7 3 4]) # [0 1 5 6 7 3 4] を 配列aの1要素として追加
>>> a.append([0 1 2 8 4])
>>> a.append(a[0])
>>> a  # 配列aの中身を見る
[[0, 1, 2, 3, 4], [0, 1, 5, 6, 7, 3, 4], [0, 1, 2, 8, 4], [0, 1, 2, 3, 4]]

>>> b.append(['私', 'は', '学生', 'です', '。'])
>>> b.append(['私', 'は', '宇宙', 'が', '好き', 'です', '。'])
>>> b.append(['私', 'は', '学生', 'でした', '。'])
>>> b.append(['私', 'は', '学生', 'です', '。'])
>>> b
[['私', 'は', '学生', 'です', '。'], ['私', 'は', '宇宙', 'が', '好き', 'です', '。'], ['私', 'は', '学生', 'でした', '。'], ['私', 'は', '学生', 'です', '。']]

>>> e = ['a','b','c','d']
>>> e
['a', 'b', 'c', 'd']

ソート

>>> k = zip(a,b,e) # zipで3つの配列をセットにする
>>> k
<zip object at 0x10ee02048>

>>> k.sort() # セットにしたものをソートして、kをソートした結果のセットに置き換える
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'zip' object has no attribute 'sort'

んんん!?
zip()とsort()は相性が悪いらしい。困った。

「ソート」部分の問題を解決

sort()の代わりに、sorted()を使う!

  • a.sort() :
    配列aを小さい順に並び替えて、その並び替えた結果を配列aとして置き換える

  • sorted(a) :
    配列aを小さい順に並び替えるが、配列aの値は変わらない

>>> k = zip(a,b,e) # zipで3つの配列をセットにする
>>> q = sorted(k) # 3つの配列をセットにしたものをソート(入力した「ソート前のセット」をソート結果に置き換えない)
>>> q
[([0, 1, 2, 3, 4], ['私', 'は', '学生', 'です', '。'], 'a'), ([0, 1, 2, 3, 4], ['私', 'は', '学生', 'です', '。'], 'd'), ([0, 1, 2, 8, 4], ['私', 'は', '学生', 'でした', '。'], 'c'), ([0, 1, 5, 6, 7, 3, 4], ['私', 'は', '宇宙', 'が', '好き', 'です', '。'], 'b')]


>>> aaa = []; bbb = []; eee = [] # aaa, bbb, eeeに空配列(list型)を代入して初期化(*これらは、配列a,b,eのソートした結果を格納するための配列)

# 配列q(listやnumpyなど型を問わない)の一番初めの要素「([0, 1, 2, 3, 4], ['私', 'は', '学生', 'です', '。'], 'a')」から順々にデータを取り出して、aa,bb,eeへ代入していく。配列を読み切ったらループ終了。
>>> for aa,bb,ee in q:
...     aaa.append(aa) # とりあえず「tab」キーでインデントをずらしてから入力
...     bbb.append(bb) # 1周目では、bbb.append(['私', 'は', '学生', 'です', '。'])と同じことをしている
...     eee.append(ee)
...                    # もうfor文は終わりだということで、エンターキーを押す

>>> aaa
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 8, 4], [0, 1, 5, 6, 7, 3, 4]]
>>> bbb
[['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'でした', '。'], ['私', 'は', '宇宙', 'が', '好き', 'です', '。']]
>>> eee
['a', 'd', 'c', 'b']


配列a(ソート前) と 配列aaa(ソート後)、配列b と 配列bbb、配列c と 配列cccを見比べると、

>>> a
[[0, 1, 2, 3, 4], [0, 1, 5, 6, 7, 3, 4], [0, 1, 2, 8, 4], [0, 1, 2, 3, 4]]
>>> aaa
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 8, 4], [0, 1, 5, 6, 7, 3, 4]]

>>> b
[['私', 'は', '学生', 'です', '。'], ['私', 'は', '宇宙', 'が', '好き', 'です', '。'], ['私', 'は', '学生', 'でした', '。'], ['私', 'は', '学生', 'です', '。']]
>>> bbb
[['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'でした', '。'], ['私', 'は', '宇宙', 'が', '好き', 'です', '。']]

>>> e
['a', 'b', 'c', 'd']
>>> eee
['a', 'd', 'c', 'b']

配列a,b,eの関係性を保ちながら(何番目の要素にきているかを揃えながら)、配列a(数字列)でソートされていることが確認できた!

ちなみに、kやqで置き換えなかったとしても、同じ結果が得られる。

>>> aaa = []; bbb = []; eee = [] # 初期化

>>> for aa,bb,ee in sorted(zip(a,b,e)):
...     aaa.append(aa)
...     bbb.append(bb)
...     eee.append(ee)
...

>>> aaa
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 8, 4], [0, 1, 5, 6, 7, 3, 4]]
>>> bbb
[['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'でした', '。'], ['私', 'は', '宇宙', 'が', '好き', 'です', '。']]
>>> eee
['a', 'd', 'c', 'b']

おまけ

このままだとみづらいので、文章(数字列)ごとにラベルを表示するようにした。

>>> ta = [] # 1つ前の数字列を格納(最初は空行列)
>>> s_list = [] # 出力する文字列をlist型で並べたもの

>>> for aa,bb,ee in sorted(zip(a,b,e)):
...     if ta != aa: # 重複してなかったら
...             tb = ''.join(bb) # 配列bbの要素を、''でくっつける
...             s_list += ['{}(={})'.format(tb,aa)] # 最初の{}にはtb, 次の{}にはaaが代入される。s_listの1要素として、文字列(配列ではない)を追加。
...             ta = aa # 1つ前の数字列を更新
...     s_list += ['-> ' + ee] # s_listの1要素として、ラベルを追加。
... 
>>> s = '\n'.join(s_list) # 配列s_listの1要素ごとに改行したものを、文字列sとする

>>> print(s) # 出力
私は学生です。(=[0, 1, 2, 3, 4])
-> a
-> d
私は学生でした。(=[0, 1, 2, 8, 4])
-> c
私は宇宙が好きです。(=[0, 1, 5, 6, 7, 3, 4])
-> b

ソースコード一式

対話モードでない形式(ファイル形式)で、一式。
@shiracamus 様より、より簡潔なプログラムを提示頂いたので差し替えました。
* 半角スペース4個でインデントを作成。

sort_keep_relation_ex.py
# データの作成
a = [
    list(range(5)),        # a.append([0,1,2,3,4])と同じこと
    [0, 1, 5, 6, 7, 3, 4], # [0 1 5 6 7 3 4] を 配列aの1要素として追加
    [0, 1, 2, 8, 4],
    [0, 1, 2, 3, 4],
]
b = [
    ['', '', '学生', 'です', ''],
    ['', '', '宇宙', '', '好き', 'です', ''],
    ['', '', '学生', 'でした', ''],
    ['', '', '学生', 'です', ''],
]
e = ['a', 'b', 'c', 'd']


# ソート
aaa, bbb, eee = zip(*sorted(zip(a, b, e)))

# aaa = [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 8, 4], [0, 1, 5, 6, 7, 3, 4]]
# bbb = [['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'です', '。'], ['私', 'は', '学生', 'でした', '。'], ['私', 'は', '宇宙', 'が', '好き', 'です', '。']]
# eee = ['a', 'd', 'c', 'b']


# 文章(数字列)ごとにラベルを表示
duplicate = None
for aa, bb, ee in zip(aaa, bbb, eee):
    if aa != duplicate: # 重複してなかったら
        print('{}(={})'.format(''.join(bb), aa))
        duplicate = aa
    print('->', ee)
4
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?