2017-01-02 8 views
1

Angenommen wählen Ich habe einen Datenrahmen:Pandas mehrere Spalten bedingt

C1 V1 C2 V2 Cond 
1 2 3 4 X 
5 6 7 8 Y 
9 10 11 12 X 

Die Anweisungen geben sollte: if Cond == X, pick C1 and C2, else pick C2 and V2.

Der Ausgang ist so etwas wie Datenrahmen:

C V 
1 2 
7 8 
9 10 

** EDIT: eine weitere Anforderung hinzu: die Anzahl der Spalten ändern können, aber einige Namensmuster folgen. Wählen Sie in diesem Fall alle Spalten mit "1", ansonsten mit "2". Ich denke, die hartcodierte Lösung funktioniert möglicherweise nicht.

+1

Mögliche doppelte von [Cre Spalte mit ELIF in Pandas] (http://stackoverflow.com/questions/18194404/create-column-with-elif-in-pandas) – e4c5

+0

'indexer = {'X': ['C1', 'V1'] , 'Y': ['C2', 'V2']}; pd.concat ([pd.DataFrame (df.loc [df.Cond == k, v] .values, Spalten = ['C', 'V']) für k, v in indexer.items()]) ' ist eine Möglichkeit, dies zu tun, aber es bewahrt nicht die Reihenfolge der Zeilen. – Abdou

Antwort

2

ich mit filter und numpy.where, für den neuen Spaltennamen verwenden extract allgemeinere Lösung versuchen zu erstellen:

#if necessary sort columns 
df = df.sort_index(axis=1) 

#filter df by 1 and 2 
df1 = df.filter(like='1') 
df2 = df.filter(like='2') 
print (df1) 
    C1 V1 
0 1 2 
1 5 6 
2 9 10 

print (df2) 
    C2 V2 
0 3 4 
1 7 8 
2 11 12 
#np.where need same shape of mask as df1 and df2 
mask = pd.concat([df.Cond == 'X']*len(df1.columns), axis=1) 
print (mask) 
    Cond Cond 
0 True True 
1 False False 
2 True True 

cols = df1.columns.str.extract('([A-Za-z])', expand=False) 
print (cols) 
Index(['C', 'V'], dtype='object') 

print (np.where(mask, df1,df2)) 
Index(['C', 'V'], dtype='object') 
[[ 1 2] 
[ 7 8] 
[ 9 10]] 

print (pd.DataFrame(np.where(mask, df1, df2), index=df.index, columns=cols)) 
    C V 
0 1 2 
1 7 8 
2 9 10 
2
  • dropCond auf Werten konzentrieren ich aus
  • reshape numpy Array Auswählen so für jede Zeile
  • Index zweite Dimension I kann
  • Index erste Dimension mit np.arange(len(df)), einmal mit einem booleschen Wert unterscheiden, mit df.Cond.ne('X').mul(1). 0 für gleich X
  • konstruieren letzten Datenrahmen

pd.DataFrame(
    df.drop('Cond', 1).values.reshape(3, 2, 2)[ 
     np.arange(len(df)), 
     df.Cond.ne('X').mul(1) 
    ], df.index, ['C', 'V']) 

    C V 
0 1 2 
1 7 8 
2 9 10 
0

Sie können versuchen, einen Ansatz ähnlich den in this post

Zunächst definiert ein paar Funktionen:

def cond(row): 
    return row['Cond'] == 'X' 

def helper(row, col_if, col_ifnot): 
    return row[col_if] if cond(row) else row[col_ifnot] 

Dann, als Suming Ihre Datenrahmen werden df genannt,

df_new = pd.DataFrame(index=df.index) 
for col in ['C', 'V']: 
    col_1 = col + '1' 
    col_2 = col + '2' 
    df_new[col] = df.apply(lambda row: helper(row, col_1, col_2), axis=1) 

Beachten Sie, dass dieser Ansatz für großen Datenrahmen langsam sein könnte, da apply nicht nutzt die Vektorisierung. Aber es sollte auch mit beliebigen Spaltennamen funktionieren (ersetzen Sie einfach ['C', 'V'] durch Ihre tatsächlichen Spaltennamen).

1

Sie können df.loc und df.append verwenden, wenn die Reihenfolge der Zeilen nicht wichtig ist.

ndf1 = df.loc[df['Cond'] == 'X', ['C1','V1']] 
ndf2 = df.loc[df['Cond'] == 'Y', ['C2','V2']] 
ndf1.columns = ['C','V'] 
ndf2.columns = ['C','V'] 

result = ndf1.append(ndf2).reset_index(drop=True) 
print(result) 
    C V 
0 1 2 
1 9 10 
2 7 8 
1

Eine weitere Option mit DataFrame.where():

df[['C1', 'V1']].where(df.Cond == "X", df[['C2', 'V2']].values) 

# C1 V1 
#0 1 2 
#1 7 8 
#2 9 10 
+1

Das ist großartig! Sehr elegante Lösung.+1 für dich! Warum habe ich nicht daran gedacht .. –