Pracuję z dataframe, który wygląda w ten sposób:
df = pd.DataFrame({'ID':['A','A','A','A','B','B','B','B'],'X':[1.2,2.1,3.8,4.5,5.8,6.2,7,8.2],'Y':[10,20,30,40,50,60,70,80],'IsTrue':[1,1,0,0,1,0,0,1],'IdxVar':[1,0,0,0,0,0,0,1]})
df["DistanceToIdx"] = np.sqrt((df.X - df.X[df.groupby('ID')['IdxVar'].transform('idxmax')].reset_index(drop=True))**2 \
+(df.Y - df.Y[df.groupby('ID')['IdxVar'].transform('idxmax')].reset_index(drop=True))**2)
Próbuję utworzyć nowy plik df, który ma jeden wiersz na wartość identyfikatora, na podstawie IdxVar
. Wystarczająco proste
newdf = df.loc[df.IdxVar==1,:]
Chcę, aby moje nowe kolumny to jakieś X0_1, X0_2, X1_1, X0_2
, gdzie Xi_j
można zobaczyć jako X
wartości for i = IsTrue
(0 lub 1) i j
wskazuje posortowany indeks DistanceToIdx
(X0_1
wskazuje wartość X
w danym identyfikatorze, który ma IsTrue = 0
i najniższą DistanceToIdx
).
Mogę to zrobić za pośrednictwem groupby()
:
groupdf = df.groupby('ID')
for name,group in groupdf:
for i in range(2):
newdf.loc[newdf.ID==name, 'X0_{}'.format(i+1)] = \
group.sort_values(by=['IsTrue','DistanceToIdx'],ascending=True)['X'].values[i]
newdf.loc[newdf.ID==name, 'X1_{}'.format(i+1)] = \
group.sort_values(by=['IsTrue','DistanceToIdx'],ascending=True)['X'].values[i+2]
Daje to pożądane wyjście, ale jeśli chcę zastosować go do większej liczby zmiennych niż pokazano tutaj, a następnie zapętlić przez 100 000 grup, moja pętla trwa zbyt długo.
Zastanawiałem się, czy można by to przyspieszyć za pomocą tylko funkcji grupowania. Moją początkową myślą było po prostu wymyślenie funkcji przestawnej, ale ponieważ chcę uporządkować nowe kolumny na podstawie istniejącego wiersza, nie jestem zbyt pewien na podstawie dokumentacji, że będzie działać.
2 odpowiedzi
Używałbym GroupBy.cumcount
aby utworzyć indeksy j
i móc obracać tabelę (DataFrame.pivot_table
) rozróżnianie w 4 kolumnach. Następnie po prostu dołącz go do ramki, gdzie IdxVar==1
, używając DataFrame.join
new_df=df.copy()
#creating columns to pivot_table and set the name of the columns
new_df['id2']=df.groupby(['ID','IsTrue']).IsTrue.cumcount()+1
#Selecting IDxVar1 --->df1
df1=df[df.IdxVar.eq(1)]
#Using pivot_table
#new_df=new_df.sort_values(by=['IsTrue','DistanceToIdx'],ascending=True)
df2=new_df.pivot_table(index='ID',columns=['id2','IsTrue'],values='X')
#join both dataframes
new_df=df1.join(df2,on='ID')
#creating the names of columns
new_df.columns =df.columns.tolist() + [f'X{i}_{j}' for j,i in df2.columns]
print(new_df)
Wyjście
ID X Y IsTrue IdxVar DistanceToIdx X0_1 X1_1 X0_2 X1_2
0 A 1.2 10 1 1 0.0 3.8 1.2 4.5 2.1
7 B 8.2 80 1 1 0.0 7.0 8.2 6.2 5.8
Jak już utworzyłeś newdf
. Wymyślam rozwiązanie, używając nsmallest
, aby uzyskać 2 najmniejsze wartości z każdej grupy, unstack
i spłaszczyć kolumny z wieloma indeksami. Na koniec wróć z powrotem do newdf
df1 = (df.set_index('X').groupby(['ID', 'IsTrue']).DistanceToIdx.nsmallest(2).
reset_index(level=-1).drop('DistanceToIdx', 1))
s = df1.groupby(level=[0,1]).cumcount().add(1)
df2 = df1.set_index(s, append=True).unstack([1,2]).sort_index(level=2, axis=1)
df2.columns = df2.columns.map('{0[0]}{0[1]}_{0[2]}'.format)
df_final = newdf.merge(df2.reset_index(), on='ID')
Out[239]:
ID X Y IsTrue IdxVar DistanceToIdx X0_1 X1_1 X0_2 X1_2
0 A 1.2 10 1 1 0.0 3.8 1.2 4.5 2.1
1 B 8.2 80 1 1 0.0 7.0 8.2 6.2 5.8
set_indes(['X','Y'])
, musisz dodać i zmienić niektóre poziomy jako.: ...reset_index(level=[-1,-2])...
Podobne pytania
Nowe pytania
python
Python to wielozadaniowy, wielozadaniowy język programowania dynamicznie typowany. Został zaprojektowany tak, aby był szybki do nauczenia się, zrozumienia i użycia oraz wymuszania czystej i jednolitej składni. Należy pamiętać, że Python 2 oficjalnie nie jest obsługiwany od 01-01-2020. Mimo to, w przypadku pytań Pythona specyficznych dla wersji, dodaj znacznik [python-2.7] lub [python-3.x]. Korzystając z wariantu Pythona (np. Jython, PyPy) lub biblioteki (np. Pandas i NumPy), należy umieścić go w tagach.
new_df=new_df.sort_values(by=['IsTrue','DistanceToIdx'],ascending=True)