Заменить значения столбцов на основе другого фрейма python pandas - лучший способ?

Примечание: для простоты я использую игрушечный пример, потому что копирование/вставка кадров данных затрудняет переполнение стека (пожалуйста, дайте мне знать, если есть простой способ сделать это).

Есть ли способ объединить значения из одного кадра данных в другой, не получая столбцы _X, _Y? Я хотел бы, чтобы значения в одном столбце заменяли все нулевые значения другого столбца.

df1: 

Name   Nonprofit    Business    Education

X      1             1           0
Y      0             1           0   <- Y and Z have zero values for Nonprofit and Educ
Z      0             0           0
Y      0             1           0

df2:

Name   Nonprofit    Education
Y       1            1     <- this df has the correct values. 
Z       1            1



pd.merge(df1, df2, on='Name', how='outer')

Name   Nonprofit_X    Business    Education_X     Nonprofit_Y     Education_Y
Y       1                1          1                1               1
Y      1                 1          1                1               1
X      1                 1          0               nan             nan   
Z      1                 1          1                1               1

В предыдущем посте я пробовал комбинировать_F_FIRST и DropNA(), но они не делают работу.

Я хочу заменить нули в df1 значениями в df2. Кроме того, я хочу, чтобы все строки с одинаковыми именами были изменены в соответствии с df2.

Name    Nonprofit     Business    Education
Y        1             1           1
Y        1             1           1 
X        1             1           0
Z        1             0           1

(необходимо уточнить: значение в столбце "Бизнес", где имя = Z, должно равняться 0.)

Мое существующее решение делает следующее: я делаю подмножество на основе имен, существующих в df2, а затем заменяю эти значения правильными значениями. Тем не менее, я хотел бы менее хакерский способ сделать это.

pubunis_df = df2
sdf = df1 

regex = str_to_regex(', '.join(pubunis_df.ORGS))

pubunis = searchnamesre(sdf, 'ORGS', regex)

sdf.ix[pubunis.index, ['Education', 'Public']] = 1
searchnamesre(sdf, 'ORGS', regex)

Ответ 1

Используйте логическую маску из isin чтобы отфильтровать df и назначить нужные значения строк из rhs df:

In [27]:

df.loc[df.Name.isin(df1.Name), ['Nonprofit', 'Education']] = df1[['Nonprofit', 'Education']]
df
Out[27]:
  Name  Nonprofit  Business  Education
0    X          1         1          0
1    Y          1         1          1
2    Z          1         0          1
3    Y          1         1          1

[4 rows x 4 columns]

Ответ 2

В [27]: Это правильно.

df.loc[df.Name.isin(df1.Name), ['Nonprofit', 'Education']] = df1[['Nonprofit', 'Education']].values

df
Out[27]:

Name  Nonprofit  Business  Education

0    X          1         1          0
1    Y          1         1          1
2    Z          1         0          1
3    Y          1         1          1

[4 строки x 4 столбца]

Вышеупомянутое будет работать только тогда, когда все строки в df1 существуют в df. Другими словами, df должен быть супер-множеством df1

Если у вас есть несколько несоответствующих строк в df в df1, вы должны следовать ниже

Другими словами, df не является надмножеством df1:

df.loc[df.Name.isin(df1.Name), ['Nonprofit', 'Education']] = 
df1.loc[df1.Name.isin(df.Name),['Nonprofit', 'Education']].values

Ответ 3

Внимание: в последней версии панд оба ответа выше не работают:

Ответ KSD вызовет ошибку:

df1 = pd.DataFrame([["X",1,1,0],
              ["Y",0,1,0],
              ["Z",0,0,0],
              ["Y",0,0,0]],columns=["Name","Nonprofit","Business", "Education"])    

df2 = pd.DataFrame([["Y",1,1],
              ["Z",1,1]],columns=["Name","Nonprofit", "Education"])   

df1.loc[df1.Name.isin(df2.Name), ['Nonprofit', 'Education']] = df2.loc[df2.Name.isin(df1.Name),['Nonprofit', 'Education']].values

df1.loc[df1.Name.isin(df2.Name), ['Nonprofit', 'Education']] = df2[['Nonprofit', 'Education']].values

Out[851]:
ValueError: shape mismatch: value array of shape (2,) could not be broadcast to indexing result of shape (3,)

и ответ EdChum даст нам неверный результат:

 df1.loc[df1.Name.isin(df2.Name), ['Nonprofit', 'Education']] = df2[['Nonprofit', 'Education']]

df1
Out[852]: 
  Name  Nonprofit  Business  Education
0    X        1.0         1        0.0
1    Y        1.0         1        1.0
2    Z        NaN         0        NaN
3    Y        NaN         1        NaN

Хорошо, он будет работать безопасно только в том случае, если значения в столбце "Имя" уникальны и отсортированы в обоих фреймах данных.

Вот мой ответ:

Способ 1:

df1 = df1.merge(df2,on='Name',how="left")
df1['Nonprofit_y'] = df1['Nonprofit_y'].fillna(df1['Nonprofit_x'])
df1['Business_y'] = df1['Business_y'].fillna(df1['Business_x'])
df1.drop(["Business_x","Nonprofit_x"],inplace=True,axis=1)
df1.rename(columns={'Business_y':'Business','Nonprofit_y':'Nonprofit'},inplace=True)

Способ 2:

df1 = df1.set_index('Name')
df2 = df2.set_index('Name')
df1.update(df2)
df1.reset_index(inplace=True)

Подробнее об обновлении. Имена столбцов обоих фреймов данных, для которых требуется установить индекс, не обязательно должны совпадать перед обновлением. Вы можете попробовать "Имя1" и "Имя2". Кроме того, это работает, даже если другая ненужная строка в df2, которая не будет обновлять df1. Другими словами, df2 не обязательно должен быть супернабором df1.

Пример:

df1 = pd.DataFrame([["X",1,1,0],
              ["Y",0,1,0],
              ["Z",0,0,0],
              ["Y",0,1,0]],columns=["Name1","Nonprofit","Business", "Education"])    

df2 = pd.DataFrame([["Y",1,1],
              ["Z",1,1],
              ['U',1,3]],columns=["Name2","Nonprofit", "Education"])   

df1 = df1.set_index('Name1')
df2 = df2.set_index('Name2')


df1.update(df2)

результат:

      Nonprofit  Business  Education
Name1                                
X           1.0         1        0.0
Y           1.0         1        1.0
Z           1.0         0        1.0
Y           1.0         1        1.0