পাইথনে একাধিক কী দ্বারা অবজেক্টগুলি কীভাবে সাজানো যায়?


97

অথবা, কার্যত, আমি কীভাবে একাধিক কী দ্বারা অভিধানের তালিকা বাছাই করতে পারি?

আমার কাছে ডিক্টসের একটি তালিকা রয়েছে:

b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0},
 {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0},
 {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0},
 {u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0},
 {u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0},
 {u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0},
 {u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}]

এবং আমাকে টোটালপয়েন্টগুলি দ্বারা বিপরীত মাল্টি কী বাছাই করতে হবে, তারপরে বিপরীত নয় TOT_PTS_Misc

এটি কমান্ড প্রম্পটে যেমন করা যেতে পারে:

a = sorted(b, key=lambda d: (-d['Total_Points'], d['TOT_PTS_Misc']))

তবে আমাকে এটি একটি ফাংশন দিয়ে চালাতে হবে, যেখানে আমি তালিকায় এবং বাছাই কীগুলি পাস করি। উদাহরণস্বরূপ def multikeysort(dict_list, sortkeys):,।

লাম্বডা লাইনটি কীভাবে ব্যবহার করা যেতে পারে যা তালিকাটিকে বাছাই করবে, একচেটিয়া সংখ্যক কীগুলির জন্য যা মাল্টিকেসোর্ট ফাংশনে প্রেরণ করা হয়েছে এবং বিবেচনায় রাখবেন যে বাছাই করা কীগুলির কোনও সংখ্যক কী থাকতে পারে এবং যেগুলি বিপরীত ধরণের প্রয়োজন তাদের চিহ্নিত করা হবে এর আগে '-' দিয়ে?

উত্তর:


73

এই উত্তর অভিধানে যে কোনও ধরণের কলামের জন্য কাজ করে - অবহেলিত কলামটির সংখ্যা হওয়া দরকার না।

def multikeysort(items, columns):
    from operator import itemgetter
    comparers = [((itemgetter(col[1:].strip()), -1) if col.startswith('-') else
                  (itemgetter(col.strip()), 1)) for col in columns]
    def comparer(left, right):
        for fn, mult in comparers:
            result = cmp(fn(left), fn(right))
            if result:
                return mult * result
        else:
            return 0
    return sorted(items, cmp=comparer)

আপনি এটিকে কল করতে পারেন:

b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0},
     {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0},
     {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0},
     {u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0},
     {u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0},
     {u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0},
     {u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}]

a = multikeysort(b, ['-Total_Points', 'TOT_PTS_Misc'])
for item in a:
    print item

উভয় কলাম অবহেলা করে চেষ্টা করুন। আপনি সাজানোর অর্ডার বিপরীত দেখতে পাবেন।

পরবর্তী: এটি পরিবর্তন করুন যাতে এটি অতিরিক্ত শ্রেণি ব্যবহার না করে ....


2016-01-17

এই উত্তরটি থেকে আমার অনুপ্রেরণা গ্রহণ করা শর্তের সাথে পুনরাবৃত্তিযোগ্য মিল থেকে প্রথম আইটেমটি পাওয়ার সর্বোত্তম উপায় কী? , আমি কোডটি ছোট করে রেখেছি:

from operator import itemgetter as i

def multikeysort(items, columns):
    comparers = [
        ((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
        for col in columns
    ]
    def comparer(left, right):
        comparer_iter = (
            cmp(fn(left), fn(right)) * mult
            for fn, mult in comparers
        )
        return next((result for result in comparer_iter if result), 0)
    return sorted(items, cmp=comparer)

আপনি যদি আপনার কোড সংশ্লেষ পছন্দ করেন।


পরে ২০১ 2016-০১-১।

এটি পাইথন 3 নিয়ে কাজ করে (যা cmpযুক্তিটি দূর করে sort):

from operator import itemgetter as i
from functools import cmp_to_key

def cmp(x, y):
    """
    Replacement for built-in function cmp that was removed in Python 3

    Compare the two objects x and y and return an integer according to
    the outcome. The return value is negative if x < y, zero if x == y
    and strictly positive if x > y.

    https://portingguide.readthedocs.io/en/latest/comparisons.html#the-cmp-function
    """

    return (x > y) - (x < y)

def multikeysort(items, columns):
    comparers = [
        ((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
        for col in columns
    ]
    def comparer(left, right):
        comparer_iter = (
            cmp(fn(left), fn(right)) * mult
            for fn, mult in comparers
        )
        return next((result for result in comparer_iter if result), 0)
    return sorted(items, key=cmp_to_key(comparer))

এই উত্তরের দ্বারা অনুপ্রাণিত পাইথন 3-এ আমার কীভাবে কাস্টম বাছাই করা উচিত?


এটি সবচেয়ে ভাল কাজ করে কারণ আমি কোনও কী বা কলামগুলিতে বিপরীতটি ব্যবহার করতে পারি। ধন্যবাদ!
সিমি

সুতরাং এটি ভাল কাজ করে। আমি পরামিতি হিসাবে তালিকা এবং স্ট্রিং সহ আমার ফাংশন কল। আমি স্ট্রিংটি প্রথমে বিভক্ত করে তারপরে বিভাজন স্ট্রিং থেকে তালিকা এবং কীগুলির তালিকা সহ মাল্টিকেসোর্টকে কল করি। কলাম নামের শুরুতে স্ট্রিংয়ের কোন আইটেমটিতে '-' রয়েছে তা বিবেচ্য নয়, কারণ এটি আইটেম বা সমস্ত আইটেমের সাথে কাজ করবে। অসাধারণ. ধন্যবাদ.
সিমি

4
ধন্যবাদ, আপনি আমার দিন বাঁচিয়েছেন!
স্যান্ডার ভ্যান লিউউইন

4
cmp()Python3 জন্য উপলব্ধ নেই, তাই আমি এখানে হিসাবে উল্লেখ করেছে নিজেকে সংজ্ঞায়িত করতে, ছিল: stackoverflow.com/a/22490617/398514
pferate

8
@ হাগডব্রাউন: আপনি cmpকীওয়ার্ডটি সরিয়েছেন , তবে cmp()ফাংশনটি এখনও উপরে 4 টি লাইন ব্যবহার করা হয়েছে। আমি এটি 3.2, 3.3, 3.4 এবং 3.5 দিয়ে চেষ্টা করেছি, তারা সবাই ফাংশন কলে ব্যর্থ হয়েছিল, কারণ cmp()সংজ্ঞায়িত হয়নি is তৃতীয় বুলেট এখানে ( docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons ) চিকিত্সা উল্লেখ cmp()হিসাবে চলে গেছে।
পেফেট করুন

56

এটি করার জন্য বিভিন্ন কৌশল সম্পর্কে এই নিবন্ধটিতে একটি দুর্দান্ত রিডাউন রয়েছে। যদি আপনার প্রয়োজনীয়তাগুলি "সম্পূর্ণ দ্বিপাক্ষিক মাল্টিকি" এর চেয়ে সহজ হয় তবে একবার দেখুন। এটি স্বীকৃত উত্তরটি পরিষ্কার করেছে এবং আমি যে ব্লগ পোস্টটি স্রেফ উল্লেখ করেছি তা একে অপরকে কিছুটা প্রভাবিত করেছে, যদিও কোন আদেশটি আমি জানি না।

যদি লিঙ্কটি এখানে মারা যায় তবে খুব দ্রুত সংক্ষিপ্তসার উদাহরণগুলির উপরে বর্ণিত নয়:

mylist = sorted(mylist, key=itemgetter('name', 'age'))
mylist = sorted(mylist, key=lambda k: (k['name'].lower(), k['age']))
mylist = sorted(mylist, key=lambda k: (k['name'].lower(), -k['age']))

যতটুকু আমি বলতে পারি, স্টাইলজিয়ানভিশনটি আমার কোড ব্যবহার করে এবং কোনও ক্রেডিট দেয় না। গুগলের জন্যresult = cmp(fn(left), fn(right))
হিউডব্রাউন

4
সংক্ষিপ্তসার জন্য ধন্যবাদ, লিংক আসলে এখন মারা গেছে। :)
অ্যামিথ

49

আমি জানি এটি একটি পুরানো প্রশ্ন, তবে উত্তরের কোনওটিতেই উল্লেখ করা হয়নি যে পাইথন তার বাছাই করা রুটিনগুলির জন্য যেমন একটি স্থিতিশীল সাজানোর গ্যারান্টি দেয় list.sort()এবং sorted()যার অর্থ সমান তুলনা করা আইটেমগুলি তাদের মূল ক্রম ধরে রাখে।

এর অর্থ হল ORDER BY name ASC, age DESCঅভিধানের তালিকার জন্য (এসকিউএল স্বরলিপি ব্যবহার করে) এর সমতুল্য এইভাবে করা যেতে পারে:

items.sort(key=operator.itemgetter('age'), reverse=True)
items.sort(key=operator.itemgetter('name'))

আইটেমগুলি প্রথমে "কম" বৈশিষ্ট্য age(উতরাই) দ্বারা বাছাই করা হয়েছে তা নোট করুন , তারপরে "মেজর" অ্যাট্রিবিউট দ্বারা nameসঠিক চূড়ান্ত ক্রমের দিকে নিয়ে যাওয়া।

বিপরীত / বিপরীতকরণ কেবলমাত্র একটি সংখ্যার জন্য নয় যা আপনি সামনে বিয়োগ চিহ্ন সরিয়ে রেখে প্রত্যাখ্যান করতে পারেন সমস্ত অরিবেডিয়াল ধরণের জন্য কাজ করে।

এবং (কমপক্ষে) সিপিথনে টিমসোর্ট অ্যালগরিদম ব্যবহার করার কারণে এটি বাস্তবে পরিবর্তে দ্রুত।


4
খুব সুন্দর. মাঝারি ডেটা সেটগুলির জন্য যেখানে একাধিকবার সেট বাছাই করা কোনও ব্যাপার নয়, এটি দুর্দান্ত দুর্দান্ত! আপনি উল্লেখ হিসাবে, আপনি স্কাইল সাজানোর তুলনায় পাইথন সাজান বিপরীত করতে হবে। ধন্যবাদ
গ্রেগ

দ্বিতীয় ধরণের প্রথমটির ফলাফলটি ভেঙে যাবে। মজার বিষয় যে upvoters কেউ এটি লক্ষ্য করে নি।
আগ্নেয়গিরি

9
মজার বিষয় যে আপনি খেয়াল করেন নি যে প্রাথমিক সাজানোর মানদণ্ডটি সর্বশেষে চলে যায়, যেমনটি আমার উদাহরণে দেখানো হয়েছে, এবং স্পষ্টভাবে অন্য মন্তব্যে উল্লিখিতভাবে উল্লেখ করেছেন যে আপনি যদি বিষয়টি খেয়াল না করেন তবে এটি খুব স্পষ্ট করে দিতে।
ওয়াউটার বলস্টারলি

24
def sortkeypicker(keynames):
    negate = set()
    for i, k in enumerate(keynames):
        if k[:1] == '-':
            keynames[i] = k[1:]
            negate.add(k[1:])
    def getit(adict):
       composite = [adict[k] for k in keynames]
       for i, (k, v) in enumerate(zip(keynames, composite)):
           if k in negate:
               composite[i] = -v
       return composite
    return getit

a = sorted(b, key=sortkeypicker(['-Total_Points', 'TOT_PTS_Misc']))

কি দারুন! ঐটা অসাধারণ. এটি দুর্দান্ত কাজ করে। আমি এমন নবাগত যে আমার মনে হয় আমি এই সমস্ত কিছু জানার পক্ষে পৌঁছতে পারব না। তাও ছিল দ্রুত। আপনাকে অনেক ধন্যবাদ.
সিমি

তবে, যদি সর্দটকিপিকারে প্রেরিত কীগুলি '-টোটালপয়েন্টস, টোপ্পিটিস_মিস্ক' এর মতো স্ট্রিং হয়?
সিমি

4
তারপরে আপনি স্ট্রিংটিকে প্রথমে অ্যারেতে ভাগ করতে পারেনsome_string.split(",")
জেসন ক্রেইটন

ধন্যবাদ. আমি বুঝতে পেরেছি যে আমি ইতিমধ্যে মন্তব্য করার পরে আমি স্ট্রিংয়ের বিভাজন করতে পারি। ডিওএইচ!
সিমি

4
তবে আপনি যদি সংখ্যার মানের পরিবর্তে স্ট্রিং মানটিকে অবহেলা করেন? আমি মনে করি না যে এটি কাজ করবে।
নিক পার্কিনস

5

আমি কয়েকটি কলামে 2 ডি অ্যারে বাছাই করার জন্য নিম্নলিখিতটি ব্যবহার করি

def k(a,b):
    def _k(item):
        return (item[a],item[b])
    return _k

এটি কোনও ইচ্ছামত সংখ্যক আইটেমের কাজ করার জন্য বাড়ানো যেতে পারে। আমি মনে করি আপনার শৃঙ্খলাবদ্ধ কীগুলিতে আরও ভাল অ্যাক্সেস প্যাটার্ন সন্ধান করা অভিনব তুলনামূলক লেখার চেয়ে ভাল।

>>> data = [[0,1,2,3,4],[0,2,3,4,5],[1,0,2,3,4]]
>>> sorted(data, key=k(0,1))
[[0, 1, 2, 3, 4], [0, 2, 3, 4, 5], [1, 0, 2, 3, 4]]
>>> sorted(data, key=k(1,0))
[[1, 0, 2, 3, 4], [0, 1, 2, 3, 4], [0, 2, 3, 4, 5]]
>>> sorted(a, key=k(2,0))
[[0, 1, 2, 3, 4], [1, 0, 2, 3, 4], [0, 2, 3, 4, 5]]

4

আমার আজ একই ধরণের সমস্যা ছিল - আমাকে সংখ্যার মানগুলি অবতরণ করে এবং আরোহণের স্ট্রিংয়ের মান দ্বারা অভিধানের আইটেমগুলি বাছাই করতে হয়েছিল। বিরোধী দিকনির্দেশগুলির ইস্যু সমাধান করার জন্য, আমি পূর্ণসংখ্যার মানগুলিকে অবহেলা করেছি।

এখানে আমার সমাধানের বৈকল্পিক - ওপিতে প্রয়োগযোগ্য

sorted(b, key=lambda e: (-e['Total_Points'], e['TOT_PTS_Misc']))

খুব সহজ - এবং একটি কবজ মত কাজ করে

[{'TOT_PTS_Misc': 'Chappell, Justin', 'Total_Points': 96.0},
 {'TOT_PTS_Misc': 'Russo, Brandon', 'Total_Points': 96.0},
 {'TOT_PTS_Misc': 'Utley, Alex', 'Total_Points': 96.0},
 {'TOT_PTS_Misc': 'Foster, Toney', 'Total_Points': 80.0},
 {'TOT_PTS_Misc': 'Lawson, Roman', 'Total_Points': 80.0},
 {'TOT_PTS_Misc': 'Lempke, Sam', 'Total_Points': 80.0},
 {'TOT_PTS_Misc': 'Gnezda, Alex', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Kirks, Damien', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Korecz, Mike', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Worden, Tom', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Burgess, Randy', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Harmon, Gary', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Smugala, Ryan', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Swartz, Brian', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Blackwell, Devon', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Blasinsky, Scott', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Bolden, Antonio', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Carter III, Laymon', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Coleman, Johnathan', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Kovach, Alex', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Smith, Ryan', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Venditti, Nick', 'Total_Points': 60.0}]

0
from operator import itemgetter
from functools import partial

def _neg_itemgetter(key, d):
    return -d[key]

def key_getter(key_expr):
    keys = key_expr.split(",")
    getters = []
    for k in keys:
        k = k.strip()
        if k.startswith("-"):
           getters.append(partial(_neg_itemgetter, k[1:]))
        else:
           getters.append(itemgetter(k))

    def keyfunc(dct):
        return [kg(dct) for kg in getters]

    return keyfunc

def multikeysort(dict_list, sortkeys):
    return sorted(dict_list, key = key_getter(sortkeys)

প্রদর্শন:

>>> multikeysort([{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 60.0},
                 {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0}, 
                 {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0}],
                "-Total_Points,TOT_PTS_Misc")
[{u'Total_Points': 96.0, u'TOT_PTS_Misc': u'Chappell, Justin'}, 
 {u'Total_Points': 96.0, u'TOT_PTS_Misc': u'Russo, Brandon'}, 
 {u'Total_Points': 60.0, u'TOT_PTS_Misc': u'Utley, Alex'}]

পার্সিংটি কিছুটা নাজুক, তবে কমপক্ষে এটি কীগুলির মধ্যে পরিবর্তনশীল সংখ্যক স্পেসের অনুমতি দেয়।


তবে, যখন স্ট্রিংটিতে আমার সাথে '-' দিয়ে দ্বিতীয় আইটেম থাকে, তখন এটি আমাকে আনরি - ত্রুটির জন্য একটি খারাপ অপরেন্ড টাইপ দেয়।
সিমি

আপনি একটি স্ট্রিংয়ের নেতিবাচক নিতে পারবেন না।
টর্স্টেন মেরেক

হ্যাঁ, আমি জানি, তবে এইভাবে প্যারামিটারগুলি পাস হয়ে গেছে I এমনকি আমি বিভাজনটি করলেও একটি বা অন্যটি '-' দিয়ে শুরু হবে। আমি মনে করি কী_জেটারকে কল করার আগে বাছাই করা বাছাই করা দরকার, কী তালিকার প্রতিটি আইটেম প্রথম অক্ষরটি পরীক্ষা করবে। আমি কি সঠিক পথে রয়েছি?
সিমি

0

যেহেতু আপনি ল্যাম্বডায় ইতিমধ্যে স্বাচ্ছন্দ্য বোধ করছেন তাই এখানে একটি কম ভারবস সমাধান রয়েছে।

>>> def itemgetter(*names):
    return lambda mapping: tuple(-mapping[name[1:]] if name.startswith('-') else mapping[name] for name in names)

>>> itemgetter('a', '-b')({'a': 1, 'b': 2})
(1, -2)

এটা কাজ করে না. আমার আছে: মানগুলি = ['-তুলপয়েন্টগুলি', 'TOT_PTS_Misc'] তারপরে বি dicts এর তালিকা হিসাবে খ যখন আমি g = আইটেমটেটারকে (মানগুলি) বলি (খ) আমি AttributeError পাই: 'তালিকা' অবজেক্টের কোনও 'আরম্ভের' নেই
সিমি

এটি নামের একটি পরিবর্তনশীল সংখ্যক লাগে, নামের তালিকা নয়। একে এটিকে কল করুন: আইটেমজিটার (* মান)। অন্য উদাহরণের জন্য অনুরূপ বিল্টিন অপারেটর.মিটমিটারটি দেখুন।
এ কোডি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.