Python dict показывает список значений ключей, выбирая уникальные значения других ключей - PullRequest
0 голосов
/ 27 апреля 2020

У меня есть серия имен файлов, содержащих пары key,value. Например, filename1 содержит:

A : U
B : 10
C : checksum1

Я хотел бы получить набор значений на основе выбора уникальных значений других ключей. Например, если мои значения ключей в файлах могут быть представлены как:

A  B     C         D
-------------------------
U 10 checksum1 filename1
U 10 checksum2 filename2
U 20 checksum3 filename3
V 20 checksum4 filename4
V 20 checksum5 filename5

Я хотел бы получить:

t = table.unique_values_for(["A","B"]) 
# [("U",10), ("U",20), ("V,20")]

t.result_for_unique(["C","D"]) 
# [
# [(checksum1, filename1),(checksum2 filename2)], <-result for ("U",10)
# [(checksum3, filename3)], <- result for ("U",20)
# [(checksum4, filename4), (checksum5, filename5)] <- result for ("V,20")
# ]

Я пробовал с обычными dict s, pandas, astropy.table.

Это один из тестов, которые я пробовал до сих пор:

class minidb():                                                                                                                                                    

    def __init__(self, pattern):                                                                                                                                   
        if isinstance(pattern, str):                                                                                                                               
            pattern = [pattern]                                                                                                                                    
        self.pattern = pattern                                                                                                                                     
        self.heads = [ get_fits_header(f, fast=True) for f in pattern ]                                                                                            
        keys = self.heads[0].keys()                                                                                                                                
        values = [ [ h.get(k) for h in self.heads ] for k in keys ]                                                                                                
        dic = dict(zip(keys, values))                                                                                                                              
        dic["ARP FILENAME"] = pattern # adding filename                                                                                                            
        self.dic = dic                                                                                                                                             
        self.table = Table(dic) # original                                                                                                                         
        self.data = self.table                                                                                                                                     
        self.unique = None                                                                                                                                         
        self.names = None                                                                                                                                          

    def unique_for(self, keys):                                                                                                                                    
        # if isinstance(keys, str):                                                                                                                                
        #     keys = [keys]                                                                                                                                        
        self.data = self.table.group_by(keys)                                                                                                                      
        self.unique = self.data.groups.keys.as_array().tolist()                                                                                                    
        return self.unique                                                                                                                                         

    def names_for(self, keys):                                                                                                                                     
        if isinstance(keys, str):                                                                                                                                  
            keys = [keys]                                                                                                                                          
        self.names = [ np.array(g[keys]).tolist() for g in self.data.groups]                                                                                       
        self.data = self.table[keys]                                                                                                                               
        return self.names                                                                                                                                          

Ответы [ 4 ]

1 голос
/ 28 апреля 2020

astropy.table может сделать это так же, как pandas DataFrame:

>>> text = """A  B     C         D
... -------------------------
... U 10 checksum1 filename1
... U 10 checksum2 filename2
... U 20 checksum3 filename3
... V 20 checksum4 filename4
... V 20 checksum5 filename5"""
...
>>> dat = Table.read(text, format='ascii', data_start=2)
>>> dat
<Table length=5>
 A     B       C         D    
str1 int64    str9      str9  
---- ----- --------- ---------
   U    10 checksum1 filename1
   U    10 checksum2 filename2
   U    20 checksum3 filename3
   V    20 checksum4 filename4
   V    20 checksum5 filename5
>>> list(dat.group_by(['A', 'B']).groups)
[<Table length=2>
  A     B       C         D    
 str1 int64    str9      str9  
 ---- ----- --------- ---------
    U    10 checksum1 filename1
    U    10 checksum2 filename2,
 <Table length=1>
  A     B       C         D    
 str1 int64    str9      str9  
 ---- ----- --------- ---------
    U    20 checksum3 filename3,
 <Table length=2>
  A     B       C         D    
 str1 int64    str9      str9  
 ---- ----- --------- ---------
    V    20 checksum4 filename4
    V    20 checksum5 filename5]
1 голос
/ 28 апреля 2020

Вы можете использовать itertools.groupby, чтобы сгруппировать данные по первым двум элементам. Это требует, чтобы данные были уже упорядочены по их ключам; если это не так, вы можете использовать sorted заранее.

import itertools as it

data = [
    ('U', 10, 'checksum1', 'filename1'),
    ('U', 10, 'checksum2', 'filename2'),
    ('U', 20, 'checksum3', 'filename3'),
    ('V', 20, 'checksum4', 'filename4'),
    ('V', 20, 'checksum5', 'filename5'),
]

result = [list(g) for k, g in it.groupby(data, lambda x: x[:2])]
result = [[x[2:] for x in group] for group in result]  # optionally drop the first two items
print(result)
1 голос
/ 28 апреля 2020

Pandas может сделать это легко, используя groupby:

In [1]: df = pd.DataFrame([
   ...: dict(A='U', B=10, C=1, D=1),
   ...: dict(A='U', B=10, C=2, D=2),
   ...: dict(A='U', B=20, C=3, D=3),
   ...: dict(A='V', B=20, C=4, D=4),
   ...: dict(A='V', B=20, C=5, D=5)
   ...: ])

In [2]: list(df.groupby(['A', 'B']))
Out[2]:
[(('U', 10),
     A   B  C  D
  0  U  10  1  1
  1  U  10  2  2),
 (('U', 20),
     A   B  C  D
  2  U  20  3  3),
 (('V', 20),
     A   B  C  D
  3  V  20  4  4
  4  V  20  5  5)]

Каждый элемент в этом списке является кортежем ключа (значения «A» и «B») и кадром данных (технически вид на исходный фрейм данных), содержащий только те строки, которые имеют эти значения для «A» и «B». Вы можете l oop в результатах группирования и извлекать любую информацию из "C" и "D", как вы обычно получаете данные из фрейма данных.

0 голосов
/ 28 апреля 2020

Обобщая @a_guest, у меня было:

data = [
    ('U', 10, 'checksum1', 'filename1'),
    ('U', 10, 'checksum2', 'filename2'),
    ('U', 20, 'checksum3', 'filename3'),
    ('V', 20, 'checksum4', 'filename4'),
    ('V', 20, 'checksum5', 'filename5'),
]
data = [dict(zip(('A','B','C','D'), (x))) for x in data]
# [{'A': 'U', 'B': 10, 'C': 'checksum1', 'D': 'filename1'},
#  {'A': 'U', 'B': 10, 'C': 'checksum2', 'D': 'filename2'},
#  {'A': 'U', 'B': 20, 'C': 'checksum3', 'D': 'filename3'},
#  {'A': 'V', 'B': 20, 'C': 'checksum4', 'D': 'filename4'},
#  {'A': 'V', 'B': 20, 'C': 'checksum5', 'D': 'filename5'}]

Затем "группировка по" A и B:

keys=["A","B"]
result = [ list(g) for k,g in it.groupby(data, lambda x: (tuple(x[k] for k in keys)) ) ]
# [[{'A': 'U', 'B': 10, 'C': 'checksum1', 'D': 'filename1'},
#   {'A': 'U', 'B': 10, 'C': 'checksum2', 'D': 'filename2'}],
#  [{'A': 'U', 'B': 20, 'C': 'checksum3', 'D': 'filename3'}],
#  [{'A': 'V', 'B': 20, 'C': 'checksum4', 'D': 'filename4'},
#   {'A': 'V', 'B': 20, 'C': 'checksum5', 'D': 'filename5'}]]

и "извлечение" C и D для этих групп :

names=["C","D"]
res2 = [ [ tuple(x[n] for n in names) for x in r] for r in result ]
# [[('checksum1', 'filename1'), ('checksum2', 'filename2')],
#  [('checksum3', 'filename3')],
#  [('checksum4', 'filename4'), ('checksum5', 'filename5')]]

Таким образом, представление может быть:

values = [ tuple(d.get(k) for k in keys) for d in data  ]
res3 = dict(zip(set(values),res2))
# {('U', 10): [('checksum1', 'filename1'), ('checksum2', 'filename2')],
#  ('U', 20): [('checksum3', 'filename3')],
#  ('V', 20): [('checksum4', 'filename4'), ('checksum5', 'filename5')]}

Я не знаю, можно ли упростить мои списки понимания или избежать импорта itertool. Я новичок в этом.

...