Маска кадра данных с другой многоиндексированной серией - PullRequest
0 голосов
/ 18 декабря 2018

У меня есть Dataframe, который я хотел бы замаскировать (преобразовать в NaN) с логическими значениями многоиндексированных рядов, где многоиндексный ряд также являются именами столбцов в Dataframe.Например, если df равно:

df = pd.DataFrame({ 'A': (188, 750, 1330, 1385, 188, 750, 810, 1330, 1385),
                     'B': (1, 2, 4, 5, 1, 2, 3, 4, 5),
                     'C': (2, 5, 7, 2, 5, 5, 3, 7, 2),
                     'D': ('foo', 'foo', 'foo', 'foo', 'bar', 'bar', 'bar', 'bar', 'bar') })

    A    B  C   D
0   188  1  2   foo
1   750  2  5   foo
2   1330 4  7   foo
3   1385 5  2   foo
4   188  1  5   bar
5   750  2  5   bar
6   810  3  3   bar
7   1330 4  7   bar
8   1385 5  2   bar

и многоиндексированная серия ser равна:

arrays = [('188', '750', '810', '1330', '1385'),
          ('1', '2', '3', '4', '5')]
tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['A', 'B'])
ser = pd.Series([False, False, True, False, True], index=index)

A     B
188   1    False
750   2    False
810   3    True
1330  4    False
1385  5    True
dtype: bool

, как я могу замаскировать (преобразовать в NaN) значение настолбец C в df, где записи являются False в Серии ser, чтобы закончить окончательным кадром данных, который будет выглядеть следующим образом:

    A    B  C   D
0   188  1  2   foo
1   750  2  5   foo
2   1330 4  7   foo
3   1385 5  NaN foo
4   188  1  5   bar
5   750  2  5   bar
6   810  3  NaN bar
7   1330 4  7   bar
8   1385 5  NaN bar

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Измените шаг инициализации для ser:

arrays = [('188', '750', '810', '1330', '1385'),
          ('1', '2', '3', '4', '5')]
# Note: The change is in this step - make the levels numeric.
tuples = list(zip(*map(pd.to_numeric, arrays)))
index = pd.MultiIndex.from_tuples(tuples, names=['A', 'B'])
ser = pd.Series([False, False, True, False, True], index=index)

Инициализируйте уровни index, чтобы иметь тот же тип d, что и для 'A' и 'B'.Надеюсь, это не должно быть проблемой.

Это позволит нам построить гораздо более простое решение с использованием loc и выбора и назначения на основе индекса.

u = df.set_index(['A', 'B'])
u.loc[ser.index[ser], 'C'] = np.nan

u.reset_index()
      A  B    C    D
0   188  1  2.0  foo
1   750  2  5.0  foo
2  1330  4  7.0  foo
3  1385  5  NaN  foo
4   188  1  5.0  bar
5   750  2  5.0  bar
6   810  3  NaN  bar
7  1330  4  7.0  bar
8  1385  5  NaN  bar

Если вы столкнулись с ситуацией, в которой вы находитесьучитывая ser и необходимость изменить d-тип индекса, вы можете быстро перестроить его, используя понимание списка внутри pd.Index.set_levels.

ser.index = ser.index.set_levels([l.astype(int) for l in ser.index.levels]) 
# Alternative,
# ser.index = ser.index.set_levels([
#     pd.to_numeric(l) for l in ser.index.levels]) 

Теперь это работает:

u = df.set_index(['A', 'B'])
u.loc[ser.index[ser], 'C'] = np.nan

u.reset_index()

      A  B    C    D
0   188  1  2.0  foo
1   750  2  5.0  foo
2  1330  4  7.0  foo
3  1385  5  NaN  foo
4   188  1  5.0  bar
5   750  2  5.0  bar
6   810  3  NaN  bar
7  1330  4  7.0  bar
8  1385  5  NaN  bar

Обратите внимание на шаг индексации ser.index[ser] в loc, мы используем индекс ser вместо index напрямую.

0 голосов
/ 18 декабря 2018

Используйте isin для проверки членства между обоими MultiIndex:

#convert columns to strings for same types of levels
df[['A','B']] = df[['A','B']].astype(str)
df.loc[df.set_index(['A','B']).index.isin(ser.index[ser]), 'C'] = np.nan
print (df)
      A  B    C    D
0   188  1  2.0  foo
1   750  2  5.0  foo
2  1330  4  7.0  foo
3  1385  5  NaN  foo
4   188  1  5.0  bar
5   750  2  5.0  bar
6   810  3  NaN  bar
7  1330  4  7.0  bar
8  1385  5  NaN  bar
0 голосов
/ 18 декабря 2018

Использование:

# Converting ser to a dataframe 
ndf = pd.DataFrame(ser).reset_index()

# Fetching B values against which C values needs to be mapped to NaN
idx = ndf[ndf.iloc[:,2] == True].B.values

# Fetching df index where C values needs to be mapped to NaN
idx_ = df[df.B.isin(idx)].index

# Mapping of C values to NaN
df.loc[idx_,'C'] = np.NaN


+---+------+---+-----+-----+
|   |   A  | B |  C  |  D  |
+---+------+---+-----+-----+
| 0 |  188 | 1 | 2.0 | foo |
| 1 |  750 | 2 | 5.0 | foo |
| 2 | 1330 | 4 | 7.0 | foo |
| 3 | 1385 | 5 | NaN | foo |
| 4 |  188 | 1 | 5.0 | bar |
| 5 |  750 | 2 | 5.0 | bar |
| 6 |  810 | 3 | NaN | bar |
| 7 | 1330 | 4 | 7.0 | bar |
| 8 | 1385 | 5 | NaN | bar |
+---+------+---+-----+-----+
...