Pandalar DataFrame'i iç içe geçmiş sözlükteki öğelerden oluşturun


90

Şu yapıyla iç içe geçmiş bir 'user_dict' sözlüğüm olduğunu varsayalım:

  • Seviye 1: Kullanıcı Kimliği (Uzun Tamsayı)
  • Seviye 2: Kategori (Dize)
  • Seviye 3: Çeşitli Nitelikler (yüzer, tam sayılar, vb.)

Örneğin, bu sözlüğün bir girişi şöyle olacaktır:

user_dict[12] = {
    "Category 1": {"att_1": 1, 
                   "att_2": "whatever"},
    "Category 2": {"att_1": 23, 
                   "att_2": "another"}}

içindeki her öğe user_dictaynı yapıya sahip ve user_dictbir pandanın DataFrame'ine beslemek istediğim çok sayıda öğe içeriyor, bu da özelliklerden seriyi oluşturuyor. Bu durumda hiyerarşik bir indeks amaç için faydalı olacaktır.

Spesifik olarak, benim sorum, DataFrame yapıcısının serinin sözlükteki "seviye 3" değerlerinden oluşturulması gerektiğini anlamasına yardımcı olacak bir yol olup olmadığıdır.

Şöyle bir şey denersem:

df = pandas.DataFrame(users_summary)

"Düzey 1" deki (UserId'ler) öğeler sütunlar olarak alınır, bu da elde etmek istediğim şeyin tam tersidir (UserId'ler indeks olarak olsun).

Sözlük girişlerini yineledikten sonra seriyi oluşturabileceğimi biliyorum, ancak daha doğrudan bir yol varsa, bu çok yararlı olacaktır. Benzer bir soru, bir dosyada listelenen json nesnelerinden pandalar DataFrame oluşturmanın mümkün olup olmadığını sormak olacaktır.


Daha basit alternatifler için bu yanıta bakın .
cs95

Yanıtlar:


141

Pandalar MultiIndex, tuple listesinden oluşur. Bu yüzden en doğal yaklaşım, girdi diktenizi yeniden şekillendirmek olacaktır, böylece anahtarları, ihtiyaç duyduğunuz çoklu dizin değerlerine karşılık gelen dizelerdir. Ardından pd.DataFrame.from_dictşu seçeneği kullanarak veri çerçevenizi oluşturabilirsiniz orient='index':

user_dict = {12: {'Category 1': {'att_1': 1, 'att_2': 'whatever'},
                  'Category 2': {'att_1': 23, 'att_2': 'another'}},
             15: {'Category 1': {'att_1': 10, 'att_2': 'foo'},
                  'Category 2': {'att_1': 30, 'att_2': 'bar'}}}

pd.DataFrame.from_dict({(i,j): user_dict[i][j] 
                           for i in user_dict.keys() 
                           for j in user_dict[i].keys()},
                       orient='index')


               att_1     att_2
12 Category 1      1  whatever
   Category 2     23   another
15 Category 1     10       foo
   Category 2     30       bar

Alternatif bir yaklaşım, veri çerçevenizi bileşen veri çerçevelerini birleştirerek oluşturmak olabilir:

user_ids = []
frames = []

for user_id, d in user_dict.iteritems():
    user_ids.append(user_id)
    frames.append(pd.DataFrame.from_dict(d, orient='index'))

pd.concat(frames, keys=user_ids)

               att_1     att_2
12 Category 1      1  whatever
   Category 2     23   another
15 Category 1     10       foo
   Category 2     30       bar

11
Bunu, keyfi derinlikte düzensiz listelerle çalışmak için genellemenin makul bir yolu var mı? Örneğin, bazı dalların diğerlerinden daha kısa olabileceği keyfi bir derinliğe sahip listeler ve daha kısa dallar sona ulaşmadığında Yok veya nan kullanılır?
naught101

5
Pandas json desteğine (io araçları) ve normalleştirmeye baktınız mı? pandas.pydata.org/pandas-docs/dev/io.html#normalization
Wouter Overmeire

1
benim için ilk yöntem, tuple'lı tek bir indeksi olan bir veri çerçevesi oluşturdu. ikinci yöntem istendiği / beklendiği gibi çalıştı!
arturomp

Bu yeni sütunların nasıl adlandırılacağına dair herhangi bir ipucu? Örneğin, bu 12 ve 15 sayılarının 'id' sütununda olmasını istersem.
cheremushkin

1
@cheremushkin 12 ve 15 artık 'id' satırındadır, eğer aktarırsanız ( pandas.pydata.org/pandas-docs/stable/reference/api/… ) bunlar 'id' sütunundadır. Ayrıca yığınları kaldırabilirsiniz ( pandas.pydata.org/pandas-docs/stable/reference/api/… ) Her şey gerçekten neye ihtiyacınız olduğuna bağlıdır.
Wouter Overmeire

33

pd.concatbir sözlüğü kabul eder. Bunu akılda tutarak, alt çerçevelere sözlük eşleme anahtarları oluşturmak için bir sözlük anlayışı kullanarak şu anda kabul edilen yanıtı basitlik ve performans açısından geliştirmek mümkündür .

pd.concat({k: pd.DataFrame(v).T for k, v in user_dict.items()}, axis=0)

Veya,

pd.concat({
        k: pd.DataFrame.from_dict(v, 'index') for k, v in user_dict.items()
    }, 
    axis=0)

              att_1     att_2
12 Category 1     1  whatever
   Category 2    23   another
15 Category 1    10       foo
   Category 2    30       bar

4
Parlak! Çok daha iyi :)
pg2455

3
Hala başka bir iç kategoriniz olsa bunu nasıl yapardınız? Gibi 12:{cat1:{cat11:{att1:val1,att2:val2}}}. Başka bir deyişle: bazıları, çözümü ilgisiz sayıda kategoriye nasıl genelleyebilir?
Lucas Aimaretto

1
@LucasAimaretto Genellikle keyfi olarak iç içe geçmiş yapılar ile düzleştirilebilir json_normalize. Nasıl çalıştığını gösteren başka bir cevabım var .
cs95

1
vÖrneğin tek bir tamsayı ise çalışmaz . Böyle bir durumda bir alternatif biliyor musunuz?
sk

11

Bu yüzden, sözlükte yinelemek için de bir for döngüsü kullanırdım, ancak çok daha hızlı çalıştığını bulduğum bir şey, bir panele ve ardından bir veri çerçevesine dönüştürmektir. Bir sözlüğün olduğunu söyle d

import pandas as pd
d
{'RAY Index': {datetime.date(2014, 11, 3): {'PX_LAST': 1199.46,
'PX_OPEN': 1200.14},
datetime.date(2014, 11, 4): {'PX_LAST': 1195.323, 'PX_OPEN': 1197.69},
datetime.date(2014, 11, 5): {'PX_LAST': 1200.936, 'PX_OPEN': 1195.32},
datetime.date(2014, 11, 6): {'PX_LAST': 1206.061, 'PX_OPEN': 1200.62}},
'SPX Index': {datetime.date(2014, 11, 3): {'PX_LAST': 2017.81,
'PX_OPEN': 2018.21},
datetime.date(2014, 11, 4): {'PX_LAST': 2012.1, 'PX_OPEN': 2015.81},
datetime.date(2014, 11, 5): {'PX_LAST': 2023.57, 'PX_OPEN': 2015.29},
datetime.date(2014, 11, 6): {'PX_LAST': 2031.21, 'PX_OPEN': 2023.33}}}

Komuta

pd.Panel(d)
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 2 (major_axis) x 4 (minor_axis)
Items axis: RAY Index to SPX Index
Major_axis axis: PX_LAST to PX_OPEN
Minor_axis axis: 2014-11-03 to 2014-11-06

pd.Panel (d) [öğe] bir veri çerçevesi verir

pd.Panel(d)['SPX Index']
2014-11-03  2014-11-04  2014-11-05 2014-11-06
PX_LAST 2017.81 2012.10 2023.57 2031.21
PX_OPEN 2018.21 2015.81 2015.29 2023.33

Daha sonra bunu bir veri çerçevesine dönüştürmek için to_frame () komutuna basabilirsiniz. Reset_index'i de büyük ve küçük ekseni indeks olarak kullanmak yerine sütunlara dönüştürmek için kullanıyorum.

pd.Panel(d).to_frame().reset_index()
major   minor      RAY Index    SPX Index
PX_LAST 2014-11-03  1199.460    2017.81
PX_LAST 2014-11-04  1195.323    2012.10
PX_LAST 2014-11-05  1200.936    2023.57
PX_LAST 2014-11-06  1206.061    2031.21
PX_OPEN 2014-11-03  1200.140    2018.21
PX_OPEN 2014-11-04  1197.690    2015.81
PX_OPEN 2014-11-05  1195.320    2015.29
PX_OPEN 2014-11-06  1200.620    2023.33

Son olarak, çerçevenin görünüşünü beğenmezseniz, to_frame () 'i çağırmadan önce görünümü değiştirmek için panelin transpoze işlevini kullanabilirsiniz http://pandas.pydata.org/pandas-docs/dev/generated buradaki belgelere bakın /pandas.Panel.transpose.html

Örnek olarak

pd.Panel(d).transpose(2,0,1).to_frame().reset_index()
major        minor  2014-11-03  2014-11-04  2014-11-05  2014-11-06
RAY Index   PX_LAST 1199.46    1195.323     1200.936    1206.061
RAY Index   PX_OPEN 1200.14    1197.690     1195.320    1200.620
SPX Index   PX_LAST 2017.81    2012.100     2023.570    2031.210
SPX Index   PX_OPEN 2018.21    2015.810     2015.290    2023.330

Bu yardımcı olur umarım.


8
Panel, Pandaların daha yeni sürümlerinde kullanımdan kaldırılmıştır (yazarken v0.23).
cs95

6

Birinin veri çerçevesini multiindex olmadan "uzun formatta" (yaprak değerleri aynı türe sahip) almak istemesi durumunda, bunu yapabilirsiniz:

pd.DataFrame.from_records(
    [
        (level1, level2, level3, leaf)
        for level1, level2_dict in user_dict.items()
        for level2, level3_dict in level2_dict.items()
        for level3, leaf in level3_dict.items()
    ],
    columns=['UserId', 'Category', 'Attribute', 'value']
)

    UserId    Category Attribute     value
0       12  Category 1     att_1         1
1       12  Category 1     att_2  whatever
2       12  Category 2     att_1        23
3       12  Category 2     att_2   another
4       15  Category 1     att_1        10
5       15  Category 1     att_2       foo
6       15  Category 2     att_1        30
7       15  Category 2     att_2       bar

(Orijinal sorunun muhtemelen (I.) Düzey 1 ve 2'nin çoklu dizin olarak ve Düzey 3'ün sütun olarak olmasını istediğini ve (II.) Diktteki değerler üzerinde yinelemeden başka yollar sorduğunu biliyorum. Ama umarım bu yanıt yine de geçerlidir ve yararlı (I.): benim gibi iç içe dikteyi bu şekle sokmanın bir yolunu bulmaya çalışan ve google yalnızca bu soruyu döndüren kişilere ve (II.): çünkü diğer yanıtlar da biraz yineleme içeriyor ve ben bunu buluyorum yaklaşım esnek ve okunması kolay; yine de performans konusunda emin değilim.)


0

Doğrulanmış yanıta dayanarak, bu benim için en iyi sonucu verdi:

ab = pd.concat({k: pd.DataFrame(v).T for k, v in data.items()}, axis=0)
ab.T
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.