くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

オブジェクト

Python ソート色々(オブジェクト、辞書のリストのソート)

公式のソートHowTo

色々と。

単一項目でのソートはまー良いんだけど、

複数項目で、昇順/降順を個別指定してのソートは

サンプルとかあまり見なかったので↓の感じにした。

simple_list = [5,10,1,4,8,0]
tuple_list = [(3,'b'),(10,'x'),(5,'z')]

class TestItem:
    def __init__(self, **kwargs):
        self.id = kwargs["id"]
        self.name = kwargs["name"]
        self.val = kwargs["val"]
    def __repr__(self):
        return repr((self.id, self.name, self.val))

obj_list = []
obj_list.append(TestItem(id=10,name="y",val=1024))
obj_list.append(TestItem(id=1,name="z",val=256))
obj_list.append(TestItem(id=5,name="x",val=128))
obj_list.append(TestItem(id=101,name="y",val=512))
obj_list.append(TestItem(id=102,name="y",val=64))

dict_list = []
dict_list.append({"id":10, "name":"y", "val":1024})
dict_list.append({"id":1, "name":"z", "val":256})
dict_list.append({"id":5, "name":"x", "val":128})
dict_list.append({"id":101, "name":"y", "val":512})
dict_list.append({"id":102, "name":"y", "val":64})


from operator import attrgetter


if __name__ == "__main__":
    # 普通にソート
    print(sorted(simple_list))
    # → [0, 1, 4, 5, 8, 10]

    # 降順にソート
    print(sorted(simple_list, reverse=True))
    # → [10, 8, 5, 4, 1, 0]

    # タプルの配列のソート(タプルの最初の項目でソート)
    print(sorted(tuple_list))
    # → [(3, 'b'), (5, 'z'), (10, 'x')]

    # タプルの配列の降順でソート(タプルの指定の項目でソート)
    print(sorted(tuple_list, key=lambda x: x[1], reverse=True))
    # → [(5, 'z'), (10, 'x'), (3, 'b')]

    # オブジェクトの配列のソート
    print(sorted(obj_list, key=lambda x: x.name))
    # → [(5, 'x', 128), (10, 'y', 1024), (101, 'y', 512), (102, 'y', 64), (1, 'z', 256)]
    
    # オブジェクトの配列のソート(attrgetter使用)
    print(sorted(obj_list, key=attrgetter("name")))
    # → [(5, 'x', 128), (10, 'y', 1024), (101, 'y', 512), (102, 'y', 64), (1, 'z', 256)]

    # 辞書の配列のソート
    print(sorted(dict_list, key=lambda x: x["name"]))
    # → [{'id': 5, 'name': 'x', 'val': 128}, {'id': 10, 'name': 'y', 'val': 1024}, {'id': 101, 'name': 'y', 'val': 512}, {'id': 102, 'name': 'y', 'val': 64}, {'id': 1, 'name': 'z', 'val': 256}]

    # ここから複合ソート

    # 複合ソート用関数
    def multisort(xs, specs, fkey):
        for key, reverse in reversed(specs):
            xs.sort(key=fkey(key), reverse=reverse)
        return xs
    
    # key関数を作る高階関数(オブジェクト用)
    def fobj(key):
        return attrgetter(key)

    # key関数を作る高階関数(dict用)
    def fdict(key):
        return lambda x: x[key]

    # オブジェクトの配列の複合ソート
    print(multisort(obj_list, [('name', False), ('val', False)], fobj))
    # → [(5, 'x', 128), (102, 'y', 64), (101, 'y', 512), (10, 'y', 1024), (1, 'z', 256)]

    # 辞書の配列の複合ソート
    print(multisort(dict_list, [('name', False), ('val', False)], fdict))
    # → [{'id': 5, 'name': 'x', 'val': 128}, {'id': 102, 'name': 'y', 'val': 64}, {'id': 101, 'name': 'y', 'val': 512}, {'id': 10, 'name': 'y', 'val': 1024}, {'id': 1, 'name': 'z', 'val': 256}]

で、Pythonのソートは安定(同一値の場合に元の並び順のままになる)なのを

保証してるみたいなので、上のロジックで複数キーのソートが出来るとさ。。。

Python 内包表記まとめ

完全にメモ。

import random,string,time
from operator import itemgetter, attrgetter

def randomname(n):
   return ''.join(random.choices(string.ascii_letters + string.digits, k=n))

class SampleClass:
    def __init__(self,name,val,hoge):
        self.name = name
        self.val = val
        self.hoge = hoge

# 単純なList
numl = [random.randint(1, 1000) for i in range(100)]
selected = [v for v in numl if v < 500]
print(selected)

# 辞書
dicl = [{"key": randomname(5),"val":random.randint(1, 1000)} for i in range(100)]
selected = [v for v in dicl if v["val"] < 500]
print(selected)

# 辞書から特定要素の配列を作る
dicl = [{"key": randomname(5),"val":random.randint(1, 1000)} for i in range(100)]
selected = [v["key"] for v in dicl if v["val"] < 500]
print(selected)

# タプル
tupl = [(randomname(5),random.randint(1, 100), random.randint(200, 300) ,) for i in range(100)]
selected = [v for v in tupl if v[1] < 50]
print(selected)

# タプルのリストから部分的な要素にしたタプルのリストにする
tupl = [(randomname(5),random.randint(1, 100), random.randint(200, 300) ,) for i in range(100)]
selected = [(v[0],v[2],) for v in tupl if v[1] < 50]
print(selected)

# オブジェクトから特定フィールドのタプルにしつつ、リストにする
objl = [SampleClass(randomname(5),random.randint(1, 100),random.randint(200, 500)) for i in range(100)]
selected = [attrgetter("name","hoge")(v) for v in objl if v.val < 50]
print(selected)

Python Listのソートまとめ

list.sort()と sorted(list)の 2 つ。

まずは → を読むべし。https://docs.python.org/ja/3/howto/sorting.html

オブジェクトの配列をソートするとかする場合はkey関数を使う。

性能とか

import random
import time

time_sta = time.perf_counter()
for i in range(10000):
    numl = [random.randint(1, 1000) for i in range(100)]
    numl.sort()
time_end = time.perf_counter()
tspan = time_end- time_sta
print(tspan)

time_sta = time.perf_counter()
for i in range(10000):
    numl = [random.randint(1, 1000) for i in range(100)]
    sorted(numl)
time_end = time.perf_counter()
tspan = time_end- time_sta
print(tspan)

# どっちも大差無
3.690590940999982
3.6823944669999946

昇順/降順

numl = [random.randint(1, 1000) for i in range(100)]
print(numl.sort(reverse=True))
print(sorted(numl, reverse=True))

両者の違い

numl = [random.randint(1, 1000) for i in range(10)]
print(numl)
print(numl.sort())
print(numl)

[746, 897, 3, 989, 2, 216, 666, 852, 387, 409]
None
[2, 3, 216, 387, 409, 666, 746, 852, 897, 989]

numl = [random.randint(1, 1000) for i in range(10)]
print(numl)
print(sorted(numl))
print(numl)

[931, 793, 220, 503, 827, 961, 114, 530, 30, 528]
[30, 114, 220, 503, 528, 530, 793, 827, 931, 961]
[931, 793, 220, 503, 827, 961, 114, 530, 30, 528]

`list.sort()`は破壊的

`sorted(list)`は非破壊的

で、基本的には`sorted()`を使った方が良さそう。

Key 関数の使い方と性能

使い方
import random,string,time
from operator import itemgetter, attrgetter

dicl = [{"key": randomname(5),"val":random.randint(1, 1000)} for i in range(5)]

print( sorted(dicl,key=lambda x:x["key"]) )
print( sorted(dicl,key=lambda x:x["val"]) )

print( sorted(dicl,key=itemgetter("key")) )
print( sorted(dicl,key=itemgetter("val")) )
性能
import random,string,time
from operator import itemgetter, attrgetter

dicl = [{"key": randomname(5),"val":random.randint(1, 1000)} for i in range(1000)]
time_sta = time.perf_counter()
for i in range(10000):
    sorted(dicl,key=lambda x:x["key"])
time_end = time.perf_counter()
tspan = time_end- time_sta
print(tspan)

time_sta = time.perf_counter()
for i in range(10000):
    sorted(dicl,key=itemgetter("key"))
time_end = time.perf_counter()
tspan = time_end- time_sta
print(tspan)

7.373934460000328
6.795032753000214

`itemgetter` 使いましょう。

色んなデータ構造でソート

# 単純なList
numl = [random.randint(1, 1000) for i in range(5)]
print(sorted(numl))
print(sorted(numl,reverse=True))

# 辞書
dicl = [{"key": randomname(5),"val":random.randint(1, 1000)} for i in range(5)]
print(sorted(dicl,key=itemgetter("key")))
print(sorted(dicl,key=itemgetter("val"),reverse=True))
print(sorted(dicl,key=itemgetter("val","key")))# 複数キーのソート

# タプル
tupl = [(randomname(5),random.randint(1, 100), random.randint(200, 300) ,) for i in range(5)]
print(sorted(tupl,key=itemgetter(0)))
print(sorted(tupl,key=itemgetter(2),reverse=True))
print(sorted(tupl,key=itemgetter(2,0)))# 複数キーのソート

# オブジェクト
objl = [SampleClass(randomname(5),random.randint(1, 100)) for i in range(5)]
print(sorted(objl,key=attrgetter("name")))
print(sorted(objl,key=attrgetter("val"),reverse=True))
print(sorted(objl,key=attrgetter("val","name")))# 複数キーのソート

class SampleClass:
    def __init__(self,name,val):
        self.name = name
        self.val = val

javascript D3使って集計とか

D3使ってオブジェクト内の集計をやりたくなった。ちょっとだけ複雑なオブジェクトでやりたい。 例えば下くらいのオブジェクトの配列で。

var hogeobj= {
    date: "2016-04-01",
    group: {
        classname: "都道府県",
        values: [
            { name1: "青森", name2: "りんご", value: 6 },
            { name1: "秋田", name2: "美人", value: 10 },
            { name1: "秋田", name2: "きりたんぽ", value: 2 },
            { name1: "宮城", name2: "笹かま", value: 3 },
            { name1: "青森", name2: "りんご", value: 8 },
            { name1: "青森", name2: "美人", value: 4 },
            { name1: "秋田", name2: "笹かま", value: 15 },
            { name1: "宮城", name2: "桜", value: 1 },
        ]
    }
}

上の感じのデータセットで、例えば、各オブジェクト内のname1のグループ毎にvalueを合計したいとか。下の感じ。

var groupCountObj = d3.nest()
                            .key(function (d) { return d.name1; })
                            .rollup(function (v) { return d3.sum(v, function (d) { return +d.value; }); })
                            .map(hogeobj.group.values);
console.log(groupCountObj);
->Object {青森: 18, 秋田: 27, 宮城: 4}

集計はされるんだけど、Objectで結果が戻ってくるので割と扱いにくい。なので、配列にkey-value的な感じでmapする。

var tmpob = $.map(groupCountObj, function (value, index) {
    return { label: index, value: value }
});
console.log(tmpob);
->[Object, Object, Object]
0:Object
label:"青森"
value:18
__proto__:Object

1:Object
label:"秋田"
value:27
__proto__:Object

2:Object
label:"宮城"
value:4
__proto__:Object

length:3
__proto__:Array[0]

これでD3使っての円グラフとかに使いやすくなる。

久しぶりにD3使ったけど、すごい忘れてる。。。えらいこっちゃ。

問合せ