2017-07-17 34 views
3

我有一個DataFrame像:如何從其他替換數據幀的元素所指示的列

df = pd.DataFrame([{'v1':'a', 'v2':'b', 'v3':'1'}, 
        {'v1':'2', 'v2':'c', 'v3':'d'}]) 

v1 v2 v3 
0 a b 1 
1 2 c d 

當列的內容/行爲「1」,「2 '或'3',我想將其內容替換爲指定欄中的相應項目。即,在第一行中,列v3的值爲"1",因此我想用列v1中的第一個元素的值替換它。這樣做的兩行,我應該得到:

v1 v2 v3 
0 a b a 
1 c c d 

我可以用下面的代碼做到這一點:

for i in range(3): 
    for j in range(3): 
     df.loc[df['v%d' % (i+1)]==('%d' % (j+1)),'v%d' % (i+1)]= \ 
      df.loc[df['v%d' % (i+1)]==('%d' % (j+1)),'v%d' % (j+1)] 

有一個不太麻煩的方式來做到這一點?

回答

1
df.apply(lambda row: [row['v'+v] if 'v'+v in row else v for v in row], 1) 

這遍歷每一行和每一取代任何值v與列名爲'v'+v的值存在(如果該列存在),否則不會更改該值。

輸出:

v1 v2 v3 
0 a b a 
1 c c d 

注意,這不會限制替換爲僅數字。例如,如果您有一列名爲'va'的列,它將使用該行中的'va'列中的值替換包含'a'的所有單元格。要限制可以替換的行,可以定義可接受列名稱的列表。例如,假設你只是想實現從v1列替換:

acceptable_columns = ['v1'] 

df.apply(lambda row: [row['v'+v] if 'v'+v in acceptable_columns else v for v in row], 1) 

輸出:

v1 v2 v3 
0 a b a 
1 2 c d 

編輯

有人指出的是,以上的答案將引發一個錯誤你的數據框中有非字符串類型。您可以通過每個單元格的值顯式地轉換爲字符串避免這種情況:

df.apply(lambda row: [row['v'+str(v)] if 'v'+str(v) in row else v for v in row], 1) 

ORIGINAL(錯誤)請回答以下

注意,下面只是答案適用於當值替換上一個對角線(這是在示例中的情況,但是這不是問這個問題......我的壞)

可以與大熊貓replace方法和numpy的的做到這一點方法:

首先選擇的值來代替,這些將是數字1到你的數據幀的長度:

to_replace = [str(i) for i in range(1,len(df)+1)] 

,每個應替換然後選擇值,這些將是對角的數據幀:

import numpy as np 
replace_with = np.diag(df) 

現在你可以做實際的更換:

df.replace(to_replace, replace_with) 

這給:

v1 v2 v3 
0 a b a 
1 c c d 

當然如果你想整個事情作爲一個班輪:

df.replace([str(i) for i in range(1,len(df)+1)], np.diag(df)) 

inplace=True關鍵字ARG添加到replace,如果你想要做的到位更換。

+0

這適用於我的示例'DataFrame',因爲對角線元素碰巧是被替代,但不是更普遍。 –

+0

我的錯誤,我誤解了這個問題。我將編輯答案。 – bunji

+0

它有麻煩,如果有任何元素'南',但非常好! –

0

您可以修改數據採樣之前的df,

data = [{'v1':'a', 'v2':'b', 'v3':'1'},{'v1':'2', 'v2':'c', 'v3':'d'}] 
mapping = {'1': 'v1', '3': 'v3', '2': 'v2'} 
for idx,line in enumerate(data): 
...  for item in line: 
...   try: 
...    int(line[item ]) 
...    data[idx][item ] = data[idx][mapping[line[item ]]] 
...   except Exception: 
...    pass 

[{'v1': 'a', 'v2': 'b', 'v3': 'a'}, {'v1': 'c', 'v2': 'c', 'v3': 'd'}] 
1

我做了這個:

df = pd.DataFrame([{'v1':'a', 'v2':'b', 'v3':'1'}, 
       {'v1':'2', 'v2':'c', 'v3':'d'}]) 

def replace_col(row, columns, col_num_dict={1: 'v1', 2: 'v2', 3: 'v3'}): 
    for col in columns: 
     x = getattr(row, col) 
     try: 
      x = int(x) 
      if int(x) in col_num_dict.keys(): 
       setattr(row, col, getattr(row, col_num_dict[int(x)])) 
     except ValueError: 
      pass 
    return row 

df = df.apply(replace_col, axis=1, args=(df.columns,)) 

它應用功能replace_col上的每一行。與其列對應的行對象的屬性被同一行中的正確值替換。由於具有多個set/get屬性函數,它看起來有些複雜,但它確實需要的卻沒有太多的開銷。

1

我看到2個選項。

環比列,然後在映射

mapping = {'1': 'v1', '3': 'v3', '2': 'v2'} 

df1 = df.copy() 
for column_name, column in df1.iteritems(): 
    for k, v in mapping.items(): 
     df1.loc[column == k, column_name] = df1.loc[column == k, v] 

DF1

v1 v2 v3 
0 a b a 
1 c c d 

環比列,然後在循環中的所有 '命中'

df2 = df.copy() 
for column_name, column in df2.iteritems(): 
    hits = column.isin(mapping.keys()) 
    for idx, item in column[hits].iteritems(): 
     df2.loc[idx, column_name] = df2.loc[idx, mapping[item]] 

DF2

v1 v2 v3 
0 a b a 
1 c c d 

如果您選擇的方式,可以減少嵌套的for循環2至1環與itertools.product

+0

不知道這是不是很麻煩,但它比我的代碼更優雅! –

+0

@TedTo從我可以告訴,第一個變化基本上與你的代碼相同,但與'映射'而不是字符串格式 –

相關問題