Мой текущий DataFrame выглядит примерно так:
Index Animal AnimalClassId
0 [{animalid:1,color:red,name:cat},{animalid:2,color:blue,name:cat2}] 1
1 [{animalid:3,color:pink,name:pig}] 2
поэтому первое животное столбца представляет собой список словарей для каждой строки. Каждая строка Animal имеет список с разной длиной.
Мой идеальный выход:
Index Animal AnimalClassId
0 {animalid:1,color:red,name:cat} 1
1 {animalid:2,color:blue,name:cat2} 1
2 {animalid:3,color:pink,name:pig} 2
Также этот набор данных чрезвычайно велик (100000+ строки), поэтому я пытаюсь избежать цикла через каждую строку. Какие-нибудь хорошие методы для быстрой реализации? Буду признателен за любые предложения!
Pandas означает "данные панели" и лучше всего работает, когда каждая серия представляет собой другое поле. Таким образом, вы можете расширять и не встраивать свои словари:
# expand dataframe
df = pd.DataFrame({'Animal': np.concatenate(df['Animal']),
'AnimalClassId': np.repeat(df['AnimalClassId'],
df['Animal'].str.len())})
# un-nested dictionaries into series
df = df.join(pd.DataFrame(df.pop('Animal').values.tolist()))
print(df)
AnimalClassId animalid color name
0 1 1 red cat
0 1 1 red cat
1 2 2 blue cat2
Вы можете сделать это с помощью:
ideal_df = df.set_index(["AnimalClassId"])["Animal"]\
.apply(pd.Series)\
.stack()\
.reset_index(level=1, drop=True)\
.reset_index()
ideal_df.columns = ["AnimalClassId", "Animal"]
(Обратите внимание, что вы можете поместить эту одну одну строку или разбить ее на отдельные строки)
Образец:
ввод (ваш пример):
df = pd.DataFrame({
"Animal": [
[{"animalid":1,"color":"red","name":"cat"}, {"animalid":2,"color":"blue","name":"cat2"}],
[{"animalid":3,"color":"pink","name":"pig"}]
],
"AnimalClassId": [1, 2]
})
print(df)
Animal AnimalClassId
0 [{'animalid': 1, 'color': 'red', 'name': 'cat'... 1
1 [{'animalid': 3, 'color': 'pink', 'name': 'pig'}] 2
выход:
print(ideal_df)
AnimalClassId Animal
0 1 {'animalid': 1, 'color': 'red', 'name': 'cat'}
1 1 {'animalid': 2, 'color': 'blue', 'name': 'cat2'}
2 2 {'animalid': 3, 'color': 'pink', 'name': 'pig'}
Если у вас больше столбцов, чем просто "AnimalClassId", вам нужно будет включить их в список, переданный в set_index
, и увеличить параметр level
переданный в reset_index
на 1 для каждого дополнительного столбца. Например, если у вас есть столбец "AnimalHabitat", вам понадобится set_index(["AnimalClassId", "AnimalHabitat"])
и reset_index(level=2, drop=True)
.
Это все равно придется перебирать ваши данные за кулисами. Поскольку данные в столбце "Животные" не являются однородными (списки переменной длины), я сомневаюсь, что есть способ расширить каждый элемент в векторе, но это будет делать трюк.
Вы должны его восстановить. Важно тщательно строить каждый столбец. Простой способ сделать это:
def refactor(df):
animals=[]
for list in df.Animal : animals.extend(list) # for O(n) operation
animalclassids=[ id for nb,id in zip(df.Animal.apply(len),df.AnimalClassId)\
for k in range(nb)]
df2= pd.DataFrame({'Animal':animals, 'AnimalClassId':animalclassids})
return df2
цикл для col 1 исключает df.Animal.sum()
который кажется O (n²).
>>> refactor(df)
Animal AnimalClassId
0 {'animalid': 1, 'color': 'red', 'name': 'cat'} 1
1 {'animalid': 2, 'color': 'blue', 'name': 'cat2'} 1
2 {'animalid': 3, 'color': 'pink', 'name': 'pig'} 2
>>> df2=pd.concat((df,)*50000)
>>> len(df2)
100000
>>> %time res=refactor(df2)
Wall time: 550 ms