2017-12-11 111 views
5

मैं शब्दकोशों जो की तरह दिखता है की एक सूची है के आधार पर शब्दकोश की सूची में मूल्य:अजगर - योग एक ही कुंजी

data = [{'stat3': '5', 'stat2': '4', 'player': '1'}, 
     {'stat3': '8', 'stat2': '1', 'player': '1'}, 
     {'stat3': '6', 'stat2': '1', 'player': '3'}, 
     {'stat3': '3', 'stat2': '7', 'player': '3'}] 

और मैं एक नेस्टेड शब्दकोश जिसका कुंजी प्राप्त करना चाहते हैं से मान रहे हैं कुंजी ('प्लेयर') और जिनके मान समेकित आंकड़ों के शब्दकोश हैं।

उत्पादन चाहिए:

{'3': {'stat3': 9, 'stat2': 8, 'player': '3'}, 
'1': {'stat3': 13, 'stat2': 5, 'player': '1'}} 

पीछा कर रहा है मेरी कोड:

{'3': {'player': '3', 'stat3': 17, 'stat2': 17}, 
'1': {'player': '1', 'stat3': 18, 'stat2': 18}} 

मैं इसे कैसे सही कर सकता है:

from collections import defaultdict 
result = {} 
total_stat = defaultdict(int) 

for dict in data: 
    total_stat[dict['player']] += int(dict['stat3']) 
    total_stat[dict['player']] += int(dict['stat2']) 
total_stat = ([{'player': info, 'stat3': total_stat[info], 
       'stat2': total_stat[info]} for info in 
       sorted(total_stat, reverse=True)]) 
for item in total_stat:  
    result.update({item['player']: item}) 
print(result) 

हालांकि, मैं यह मिल गया? या क्या अन्य दृष्टिकोण हैं?

+0

एक साइड नोट के रूप में, ऐसा लगता है कि आप डेटा के बजाय नाम के लिए नामांकित करना चाहते हैं। – Elazar

+0

'stat3 ': total_stat [info], ' stat2 ': total_stat [info]' - निश्चित रूप से यह वही मान – Elazar

उत्तर

11

देने के लिए आपका डाटा बल्कि एक DataFrame, एक प्राकृतिक pandas समाधान है:

In [34]: pd.DataFrame.from_records(data).astype(int).groupby('player').sum().T.to_dict() 

Out[34]: {1: {'stat2': 5, 'stat3': 13}, 3: {'stat2': 8, 'stat3': 9}} 
+0

आप इसे थोड़ा सा साफ़ कर सकते हैं। 'astype (int)' 'applymap' (और पढ़ने में आसान) से बहुत तेज है, और संस्करण 0.17.0 के बाद निर्दिष्ट आउटपुट प्रारूप प्राप्त करने के लिए 'ओरिएंट =' अनुक्रमणिका 'है। तो 'pd.DataFrame.from_records (डेटा) .astype (int) .groupby ('player')। Sum()। To_dict (orient = 'index')'। – miradulo

+0

धन्यवाद! मैं Astype भाग संपादित करें। –

5

बस एक अधिक नेस्टेड डिफ़ॉल्ट कारखाने का उपयोग करें:

>>> total_stat = defaultdict(lambda : defaultdict(int)) 
>>> value_fields = 'stat2', 'stat3' 
>>> for datum in data: 
...  player_data = total_stat[datum['player']] 
...  for k in value_fields: 
...   player_data[k] += int(datum[k]) 
... 
>>> from pprint import pprint 
>>> pprint(total_stat) 
defaultdict(<function <lambda> at 0x1023490d0>, 
      {'1': defaultdict(<class 'int'>, {'stat2': 5, 'stat3': 13}), 
      '3': defaultdict(<class 'int'>, {'stat2': 8, 'stat3': 9})}) 
1

यहाँ समाधान समस्या बहुत जटिल बना रहे हैं के अधिकांश। आइए इसे सरल और अधिक पठनीय बनाएं। यहां आप जाएं:

In [26]: result = {} 

In [27]: req_key = 'player' 

In [29]: for dct in data: 
    ...:  player_val = dct.pop(req_key) 
    ...:  result.setdefault(player_val, {req_key: player_val}) 
    ...:  for k, v in dct.items(): 
    ...:   result[player_val][k] = result[player_val].get(k, 0) + int(v) 

In [30]: result 
Out[30]: 
{'1': {'player': '1', 'stat2': 5, 'stat3': 13}, 
'3': {'player': '3', 'stat2': 8, 'stat3': 9}} 

यहां आप सरल और साफ हो जाते हैं। इस साधारण समस्या के लिए आयात की कोई ज़रूरत नहीं है। अब कार्यक्रम के लिए आ रहा:

result.setdefault(player_val, {'player': player_val}) 

यह "player": 3 या "player": 1 के रूप में डिफ़ॉल्ट मान सेट अगर वहाँ परिणाम में ऐसी कोई कुंजी है।

result[player_val][k] = result[player_val].get(k, 0) + int(v) 

यह सामान्य मानों वाली कुंजी के लिए मान जोड़ता है।

1

सबसे अच्छा कोड नहीं, न ही अधिक पाइथनिक, लेकिन मुझे लगता है कि आपको इसके माध्यम से चलने और यह पता लगाने में सक्षम होना चाहिए कि आपका कोड कहां गलत हो गया है।

def sum_stats_by_player(data): 
    result = {} 

    for dictionary in data: 
     print(f"evaluating dictionary {dictionary}") 

     player = dictionary["player"] 
     stat3 = int(dictionary["stat3"]) 
     stat2 = int(dictionary["stat2"]) 

     # if the player isn't in our result 
     if player not in result: 
      print(f"\tfirst time player {player}") 
      result[player] = {} # add the player as an empty dictionary 
      result[player]["player"] = player 

     if "stat3" not in result[player]: 
      print(f"\tfirst time stat3 {stat3}") 
      result[player]["stat3"] = stat3 
     else: 
      print(f"\tupdating stat3 { result[player]['stat3'] + stat3}") 
      result[player]["stat3"] += stat3 

     if "stat2" not in result[player]: 
      print(f"\tfirst time stat2 {stat2}") 
      result[player]["stat2"] = stat2 
     else: 
      print(f"\tupdating stat2 { result[player]['stat2'] + stat2}") 
      result[player]["stat2"] += stat2 

    return result 


data = [{'stat3': '5', 'stat2': '4', 'player': '1'}, 
     {'stat3': '8', 'stat2': '1', 'player': '1'}, 
     {'stat3': '6', 'stat2': '1', 'player': '3'}, 
     {'stat3': '3', 'stat2': '7', 'player': '3'}] 

print(sum_stats_by_player(data)) 
0

काउंटर

का उपयोग कर एक और संस्करण
import itertools 
from collections import Counter 

def count_group(group): 
    c = Counter() 
    for g in group: 
     g_i = dict([(k, int(v)) for k, v in g.items() if k != 'player']) 
     c.update(g_i) 
    return dict(c) 

sorted_data = sorted(data, key=lambda x:x['player']) 
results = [(k, count_group(g)) for k, g in itertools.groupby(sorted_data, lambda x: x['player'])] 

print(results) 

[('1', {'stat3': 13, 'stat2': 5}), ('3', {'stat3': 9, 'stat2': 8})] 
+1

नोट है:' groupby' को काम करने के लिए, 'डेटा' सूची को' उप-चित्र 'द्वारा क्रमबद्ध करने की आवश्यकता है ['प्लेयर'] ' –

+0

काफी सही! इस चरण को शामिल करने के लिए संपादित –

0

दो छोरों होगा अनुमति देते हैं आप के लिए:

  1. समूह एक प्राथमिक कुंजी
  2. कुल के द्वारा अपने डेटा सभी माध्यमिक जानकारी

इन दोनों कार्यों aggregate_statistics समारोह नीचे दिखाया गया में पूरा कर रहे हैं।

from collections import Counter 
from pprint import pprint 


def main(): 
    data = [{'player': 1, 'stat2': 4, 'stat3': 5}, 
      {'player': 1, 'stat2': 1, 'stat3': 8}, 
      {'player': 3, 'stat2': 1, 'stat3': 6}, 
      {'player': 3, 'stat2': 7, 'stat3': 3}] 
    new_data = aggregate_statistics(data, 'player') 
    pprint(new_data) 


def aggregate_statistics(table, key): 
    records_by_key = {} 
    for record in table: 
     data = record.copy() 
     records_by_key.setdefault(data.pop(key), []).append(Counter(data)) 
    new_data = [] 
    for second_key, value in records_by_key.items(): 
     start, *remaining = value 
     for record in remaining: 
      start.update(record) 
     new_data.append(dict(start, **{key: second_key})) 
    return new_data 


if __name__ == '__main__': 
    main() 
3

यह समाधान एक नेस्टेड शब्दकोश का उपयोग करता है। out एक {player: Counter} शब्दकोश, जहां Counter रूप में खुद को एक और शब्दकोश {stat: score}

import collections 

def split_player_stat(dict_object): 
    """ 
    Split a row of data into player, stat 

    >>> split_player_stat({'stat3': '5', 'stat2': '4', 'player': '1'}) 
    '1', {'stat3': 5, 'stat2': 4} 
    """ 
    key = dict_object['player'] 
    value = {k: int(v) for k, v in dict_object.items() if k != 'player'} 
    return key, value 

data = [{'stat3': '5', 'stat2': '4', 'player': '1'}, 
     {'stat3': '8', 'stat2': '1', 'player': '1'}, 
     {'stat3': '6', 'stat2': '1', 'player': '3'}, 
     {'stat3': '3', 'stat2': '7', 'player': '3'}] 

out = collections.defaultdict(collections.Counter) 
for player_stat in data: 
    player, stat = split_player_stat(player_stat) 
    out[player].update(stat) 
print(out) 

इस समाधान का जादू collections.defaultdict और collections.Counter वर्गों द्वारा किया जाता है, दोनों शब्दकोशों तरह बर्ताव करता है।

संबंधित मुद्दे