Обновить значение в одном столбце, если строка в другом столбце содержит что-то в списке - PullRequest
0 голосов
/ 13 сентября 2018
  id name             gender
0 13 John Smith       0
1 46 Jim Jeffries     2
2 75 Jennifer Johnson 0
3 37 Sam Adams        0
4 24 John Cleese      0
5 17 Taika Waititi    0

У меня есть много имен людей и полов в df, взятых из db актеров фильма. Пол был назначен 1 (женщина), 2 (мужчина) или 0 (не указаны). Я хотел бы прочесать и бездушно принять пол по имени. Имена будут храниться в списке и заполняться вручную. Возможно, я найду кого-нибудь с гендерно-неспецифическим именем по идентификатору и выясню сам, являются ли они мужчиной / женщиной, я бы тоже хотел сделать это:

m_names = ['John', ...]
f_names = ['Jennifer', ...]
m_ids   = ['37', ...]
f_ids   = ['', ...]

У меня есть полный контроль над циклами и np.where, но я не могу понять, как пройти через этот df, строка за строкой.

Если бы то, что было сказано выше, то, что я хочу вернуть, выглядело бы так:

for index, row in df.iterrows():
  if row['gender'] == 0:
    if   row['name'].str.contains(' |'.join(f_names)) or row['id'].str.contains('|'.join(f_ids)):
      return 1
    elif row['name'].str.contains(' |'.join(m_names)) or row['id'].str.contains('|'.join(m_ids)):
      return 2
print(df)

  id name             gender
0 13 John Smith       2
1 46 Jim Jeffries     2
2 75 Jennifer Johnson 1
3 37 Sam Adams        2
4 24 John Cleese      2
5 17 Taika Waititi    0

Обратите внимание на пробел перед '|' в условии для имен, чтобы избежать захвата любых частей фамилий.

В этот момент я сталкиваюсь с тем, как я отформатировал свои операторы if. Python не любит мое форматирование и говорит, что мои «возвраты» являются «внешней функцией». Если я изменю это на

row['gender'] = #

У меня возникают проблемы с юникодом, и я использую 'str' и 'contains'.

Ответы [ 3 ]

0 голосов
/ 13 сентября 2018

Вы можете использовать функцию Pandas isin

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.isin.html

df.loc[df.name.isin(m_names), 'gender'] = 2
0 голосов
/ 13 сентября 2018

Вы можете сначала создать и объединить логические маски. Например:

m_zero = df['gender'].eq(0)

m_name_female = df['name'].str.contains(' |'.join(f_names))
m_name_male = df['name'].str.contains(' |'.join(m_names))

m_id_female = df['id'].str.contains('|'.join(f_ids))
m_id_male = df['id'].str.contains('|'.join(m_ids))

female_mask = m_zero & (m_name_female | m_id_female)
male_mask = m_zero & (m_name_male | m_id_male)

Затем примените логику через pd.DataFrame.loc:

df.loc[female_mask, 'gender'] = 1
df.loc[male_mask, 'gender'] = 2

Или использовать вложенный numpy.where:

df['gender'] = np.where(female_mask, 1, np.where(male_mask, 2, df['gender']))

Или, если вы хотите указать скалярное значение по умолчанию, используйте numpy.select:

df['gender'] = np.select([female_mask, male_mask], [1, 2], 3)
0 голосов
/ 13 сентября 2018

Похоже, вам нужно np.select и не для циклов

df['gender'] = np.select([df.name.str.contains(" |".join(m_names)),
                          df.name.str.contains(" |".join(f_names))],
                         [2, 1], 
                         default=3)
...