Панды: заполнить нан значения на основе другого столбца с повторяющимися значениями - PullRequest
0 голосов
/ 11 ноября 2018

У меня есть две таблицы, которые я пытаюсь объединить, df_a и df_b, но в обеих отсутствуют некоторые значения. Слияние происходит нормально, однако я пытаюсь ускорить процесс заполнения значений NaN после слияния.

Данные:

df_a = pd.DataFrame(data={
    'id': [1, 2, 3, 1, 6, 5, 3],
    'name': [np.nan, 'two', 'three', 'one', np.nan, np.nan, np.nan],
    'length': ['l1', 'l2', 'l3', 'l1', 'l6', np.nan, np.nan],
    'Q1': [11, 22, 33, 11, 66, 55, 33],
    'Q2': [111, 222, 333, 111, 666, 555, 333]})

df_b = pd.DataFrame(data={
    'id': [5, 2, 4, 1, 3, 1, 7, 3],
    'name': ['five', 'two', 'four', 'one_extra', 'three', 'one', 'seven', 'three'],
    'length': ['l5', 'l2', 'l4', 'l1', 'l3', 'l1', 'l7', 'l3_extra'],
    'Q1': [2055, 2022, 2044, 2011, 2033, 2011, 2077, 2033],
    'Q2': [2055, 2022, 2044, 2011, 2033, 2011, 2077, 2033]})

df_m = df_a.merge(df_b, on=['id', 'name', 'length'], how='outer',
                  suffixes=['_a', '_b'])
Output:

       id   name length  Q1   Q2
    0   1    NaN     l1  11  111
    1   2    two     l2  22  222
    2   3  three     l3  33  333
    3   1    one     l1  11  111
    4   6    NaN     l6  66  666
    5   5    NaN    NaN  55  555
    6   3    NaN    NaN  33  333

       id       name    length    Q1    Q2
    0   5       five        l5  2055  2055
    1   2        two        l2  2022  2022
    2   4       four        l4  2044  2044
    3   1  one_extra        l1  2011  2011
    4   3      three        l3  2033  2033
    5   1        one        l1  2011  2011
    6   7      seven        l7  2077  2077
    7   3      three  l3_extra  2033  2033

        id       name    length  Q1_a   Q2_a    Q1_b    Q2_b
    0    1        NaN        l1  11.0  111.0     NaN     NaN
    3    1        one        l1  11.0  111.0  2011.0  2011.0
    9    1  one_extra        l1   NaN    NaN  2011.0  2011.0
    1    2        two        l2  22.0  222.0  2022.0  2022.0
    2    3      three        l3  33.0  333.0  2033.0  2033.0
    6    3        NaN       NaN  33.0  333.0     NaN     NaN
    11   3      three  l3_extra   NaN    NaN  2033.0  2033.0
    8    4       four        l4   NaN    NaN  2044.0  2044.0
    5    5        NaN       NaN  55.0  555.0     NaN     NaN
    7    5       five        l5   NaN    NaN  2055.0  2055.0
    4    6        NaN        l6  66.0  666.0     NaN     NaN
    10   7      seven        l7   NaN    NaN  2077.0  2077.0

Конечная точка

Я пытаюсь перевести df_m в состояние, когда любые значения NaN заполнены на основе их значений из соответствующего столбца id. Каждый раз, когда есть дубликат id, я хочу сохранить оба значения, чтобы в итоге получилось следующее df:

   id       name    length  Q1_a   Q2_a    Q1_b    Q2_b
0   1        one        l1  11.0  111.0  2011.0  2011.0
1   1  one_extra        l1  11.0  111.0  2011.0  2011.0
2   2        two        l2  22.0  222.0  2022.0  2022.0
3   3      three        l3  33.0  333.0  2033.0  2033.0
4   3      three  l3_extra  33.0  333.0  2033.0  2033.0
5   4       four        l4   NaN    NaN  2044.0  2044.0
6   5       five        l5  55.0  555.0  2055.0  2055.0
7   6        NaN        l6  66.0  666.0     NaN     NaN
8   7      seven        l7   NaN    NaN  2077.0  2077.0

Обратите внимание, что есть некоторые значения NaN, в которых была запись в одной таблице, а не в другой, например. в df_a нет записи для id=4, поэтому в Q1_a и Q2_a.

есть NaN s.

Мое решение пока

Следующее работает, но я бы хотел векторизовать этот код, так как он занимает более 4 секунд только с ~ 200 строками:

dupe_ids = pd.value_counts(df_m.id)[
    pd.value_counts(df_m.id) > 1].index.values

for dupe_id in dupe_ids:
    df_m.loc[df_m.id == dupe_id] = df_m.loc[df_m.id == dupe_id].ffill().bfill()

df_m = df_m.drop_duplicates().sort_values(by='id').reset_index(drop=True)

Я попытался векторизовать код следующим образом, но результаты были странными (см. id=4).

df_m.sort_values(by='id').groupby(
    'id').ffill().bfill().drop_duplicates().reset_index(drop=True)

Output:

       id       name    length  Q1_a   Q2_a    Q1_b    Q2_b
    0   1        one        l1  11.0  111.0  2011.0  2011.0
    1   1  one_extra        l1  11.0  111.0  2011.0  2011.0
    2   2        two        l2  22.0  222.0  2022.0  2022.0
    3   3      three        l3  33.0  333.0  2033.0  2033.0
    4   3      three  l3_extra  33.0  333.0  2033.0  2033.0
    5   4       four        l4  55.0  555.0  2044.0  2044.0
    6   5       five        l5  55.0  555.0  2055.0  2055.0
    7   6      seven        l6  66.0  666.0  2077.0  2077.0
    8   7      seven        l7   NaN    NaN  2077.0  2077.0

Две таблицы, которые я на самом деле пытаюсь объединить, являются немного более сложными, поэтому приведенный выше пример приводит к гораздо более странному поведению, чем там. Существует более 100 столбцов, и разные столбцы могут иметь значения NaN в разных местах.

Любая помощь будет принята с благодарностью!

Ответы [ 2 ]

0 голосов
/ 12 ноября 2018

Спасибо jpp за ответ.

Просто чтобы проверить, я сравнил его решение с тем, которое было в моем ответе на его ответ.

Input:
df_m = df_m.append([df_m] * 10000, ignore_index=True) 

%timeit df_r1 = df_m\
          .groupby('id').apply(lambda x: x.ffill().bfill())\
          .drop_duplicates()

%timeit df_r2 = df_m.groupby('id').ffill().groupby('id').bfill().drop_duplicates()

Output:
114 ms ± 1.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
112 ms ± 515 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Итак, кажется, с 100 000 строк это очень близко.

0 голосов
/ 11 ноября 2018

Проблема в том, что вам нужно выполнить ffill + bfill в обе группы. Ваша текущая попытка векторизации выполняет ffill только для групп.

res = df_m.sort_values('id')\
          .groupby('id').apply(lambda x: x.ffill().bfill())\
          .drop_duplicates()

print(res)

    id       name    length  Q1_a   Q2_a    Q1_b    Q2_b
0    1        one        l1  11.0  111.0  2011.0  2011.0
9    1  one_extra        l1  11.0  111.0  2011.0  2011.0
1    2        two        l2  22.0  222.0  2022.0  2022.0
2    3      three        l3  33.0  333.0  2033.0  2033.0
11   3      three  l3_extra  33.0  333.0  2033.0  2033.0
8    4       four        l4   NaN    NaN  2044.0  2044.0
5    5       five        l5  55.0  555.0  2055.0  2055.0
4    6        NaN        l6  66.0  666.0     NaN     NaN
10   7      seven        l7   NaN    NaN  2077.0  2077.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...