Pandas: как заменить цикл расширенной индексацией с двумя фреймами данных?

У меня есть следующий вариант использования, когда у меня есть фрейм данных dfp, содержащий цены на два актива a и b, и другой фрейм данных dfm, содержащий метаданные об этих активах. Предположим, у меня есть даты начала и окончания, когда я хотел бы рассмотреть эти цены и установить все остальные цены вне этих диапазонов на np.nan, поэтому у меня есть:

import pandas as pd
import numpy as np

# sample prices for two assets
dfp = pd.DataFrame(data=np.random.random_sample((20, 2)),
                   columns=['a', 'b'],
                   index=pd.date_range(end='2020-12-10', periods=20))
print(dfp)
                   a         b
2020-11-21  0.411653  0.001124
2020-11-22  0.773671  0.210065
2020-11-23  0.143332  0.090111
2020-11-24  0.062085  0.475205
2020-11-25  0.160982  0.557469
2020-11-26  0.025793  0.353725
2020-11-27  0.651929  0.794265
2020-11-28  0.266566  0.270451
2020-11-29  0.713030  0.346842
2020-11-30  0.838571  0.969477
2020-12-01  0.701627  0.480349
2020-12-02  0.946619  0.344399
2020-12-03  0.430523  0.857529
2020-12-04  0.202790  0.003393
2020-12-05  0.293010  0.250172
2020-12-06  0.172535  0.932216
2020-12-07  0.508303  0.775843
2020-12-08  0.704445  0.760226
2020-12-09  0.515398  0.193958
2020-12-10  0.219717  0.040269

# metadata information for those two assets
dfm = pd.DataFrame(data=[['a', '2020-11-22', '2020-11-29'],
                         ['b', '2020-12-01', '2020-12-07']],
                   columns=['name', 'start', 'end'])

# set all prices outside the range to np.nan in a loop :(
for index, row in dfm.iterrows():
    dfp.loc[(dfp.index < row['start']) | (row['end'] < dfp.index), row['name']] = np.nan

print(dfp)
                   a         b
2020-11-21       NaN       NaN
2020-11-22  0.773671       NaN
2020-11-23  0.143332       NaN
2020-11-24  0.062085       NaN
2020-11-25  0.160982       NaN
2020-11-26  0.025793       NaN
2020-11-27  0.651929       NaN
2020-11-28  0.266566       NaN
2020-11-29  0.713030       NaN
2020-11-30       NaN       NaN
2020-12-01       NaN  0.480349
2020-12-02       NaN  0.344399
2020-12-03       NaN  0.857529
2020-12-04       NaN  0.003393
2020-12-05       NaN  0.250172
2020-12-06       NaN  0.932216
2020-12-07       NaN  0.775843
2020-12-08       NaN       NaN
2020-12-09       NaN       NaN
2020-12-10       NaN       NaN

Можно ли (и как) здесь заменить зацикливание расширенной индексацией? если да, то как?

насколько велик dfp?

Umar.H 10.12.2020 12:24

@Manakin большой на самом деле, 40 миллионов элементов :( форма dfp составляет около 10k x 400

SkyWalker 10.12.2020 12:54

Ах, хорошо, может быть, лучше объединиться с таблицей измерений и использовать ее в качестве фильтра, чтобы вы не запрашивали 400 столбцов в таблице фактов (или это источник данных из рабочей таблицы)?

Umar.H 10.12.2020 13:15
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Данные:

np.random.seed(44)
dfp = pd.DataFrame(data=np.random.random_sample((20, 2)),
                   columns=['a', 'b'],
                   index=pd.date_range(end='2020-12-10', periods=20))
dfm = pd.DataFrame(data=[['a', '2020-11-22', '2020-11-29'],
                         ['b', '2020-12-01', '2020-12-07']],
                   columns=['name', 'start', 'end'])

dfp:

                   a         b
2020-11-21  0.834842  0.104796
2020-11-22  0.744640  0.360501
2020-11-23  0.359311  0.609238
2020-11-24  0.393780  0.409073
2020-11-25  0.509902  0.710148
2020-11-26  0.960526  0.456621
2020-11-27  0.427652  0.113464
2020-11-28  0.217899  0.957472
2020-11-29  0.943351  0.881824
2020-11-30  0.646411  0.213825
2020-12-01  0.636832  0.139146
2020-12-02  0.458704  0.873863
2020-12-03  0.258450  0.664851
2020-12-04  0.862674  0.148848
2020-12-05  0.562950  0.159155
2020-12-06  0.172895  0.104023
2020-12-07  0.202938  0.455189
2020-12-08  0.794575  0.990823
2020-12-09  0.805017  0.377415
2020-12-10  0.515737  0.058899

ДФМ:

  name       start         end
0    a  2020-11-22  2020-11-29
1    b  2020-12-01  2020-12-07
x = dfm.apply(lambda row: (dfp.index < row['start']) | (row['end'] < dfp.index),
          axis=1)
final = dfp[~pd.DataFrame({'a' : x[0], 'b' : x[1]}, index=dfp.index)]

финал:

                   a         b
2020-11-21       NaN       NaN
2020-11-22  0.744640       NaN
2020-11-23  0.359311       NaN
2020-11-24  0.393780       NaN
2020-11-25  0.509902       NaN
2020-11-26  0.960526       NaN
2020-11-27  0.427652       NaN
2020-11-28  0.217899       NaN
2020-11-29  0.943351       NaN
2020-11-30       NaN       NaN
2020-12-01       NaN  0.139146
2020-12-02       NaN  0.873863
2020-12-03       NaN  0.664851
2020-12-04       NaN  0.148848
2020-12-05       NaN  0.159155
2020-12-06       NaN  0.104023
2020-12-07       NaN  0.455189
2020-12-08       NaN       NaN
2020-12-09       NaN       NaN
2020-12-10       NaN       NaN

большое спасибо! очень умное решение :) Однако я не вижу, как кадры данных сопоставляются в части dfm.apply(...) ... это чувствительно к порядку?

SkyWalker 10.12.2020 12:58
Ответ принят как подходящий

если ваш фрейм данных не слишком велик, вы можете использовать melt и merge

затем примените условное выражение, используя np.where

df1 = pd.merge(
    pd.melt(dfp.reset_index(), id_vars = "index", var_name = "name"),
    dfm,
    on=["name"],
    how = "left",
)

df1['value_new'] = np.where( 
    (df1['index'] > df1['start']) & 
    (df1['index'] < df1['end']),
    df1['value'],
    np.nan
)

       index name     value      start        end  value_new
0  2020-11-21    a  0.460695 2020-11-22 2020-11-29        NaN
1  2020-11-22    a  0.818190 2020-11-22 2020-11-29        NaN
2  2020-11-23    a  0.869208 2020-11-22 2020-11-29   0.869208
3  2020-11-24    a  0.466557 2020-11-22 2020-11-29   0.466557
4  2020-11-25    a  0.218630 2020-11-22 2020-11-29   0.218630
5  2020-11-26    a  0.769285 2020-11-22 2020-11-29   0.769285
6  2020-11-27    a  0.066418 2020-11-22 2020-11-29   0.066418
7  2020-11-28    a  0.746973 2020-11-22 2020-11-29   0.746973
8  2020-11-29    a  0.881565 2020-11-22 2020-11-29        NaN
9  2020-11-30    a  0.856797 2020-11-22 2020-11-29        NaN
10 2020-12-01    a  0.303156 2020-11-22 2020-11-29        NaN
11 2020-12-02    a  0.152055 2020-11-22 2020-11-29        NaN
12 2020-12-03    a  0.239251 2020-11-22 2020-11-29        NaN
13 2020-12-04    a  0.579377 2020-11-22 2020-11-29        NaN
14 2020-12-05    a  0.950465 2020-11-22 2020-11-29        NaN
15 2020-12-06    a  0.017557 2020-11-22 2020-11-29        NaN
16 2020-12-07    a  0.459709 2020-11-22 2020-11-29        NaN
17 2020-12-08    a  0.235053 2020-11-22 2020-11-29        NaN
18 2020-12-09    a  0.935113 2020-11-22 2020-11-29        NaN
19 2020-12-10    a  0.121584 2020-11-22 2020-11-29        NaN
20 2020-11-21    b  0.982475 2020-12-01 2020-12-07        NaN
21 2020-11-22    b  0.006563 2020-12-01 2020-12-07        NaN
22 2020-11-23    b  0.863132 2020-12-01 2020-12-07        NaN
23 2020-11-24    b  0.059826 2020-12-01 2020-12-07        NaN
24 2020-11-25    b  0.853701 2020-12-01 2020-12-07        NaN
25 2020-11-26    b  0.494347 2020-12-01 2020-12-07        NaN
26 2020-11-27    b  0.680949 2020-12-01 2020-12-07        NaN
27 2020-11-28    b  0.247310 2020-12-01 2020-12-07        NaN
28 2020-11-29    b  0.777140 2020-12-01 2020-12-07        NaN
29 2020-11-30    b  0.552633 2020-12-01 2020-12-07        NaN
30 2020-12-01    b  0.330672 2020-12-01 2020-12-07        NaN
31 2020-12-02    b  0.295119 2020-12-01 2020-12-07   0.295119
32 2020-12-03    b  0.361580 2020-12-01 2020-12-07   0.361580
33 2020-12-04    b  0.874205 2020-12-01 2020-12-07   0.874205
34 2020-12-05    b  0.754738 2020-12-01 2020-12-07   0.754738
35 2020-12-06    b  0.135053 2020-12-01 2020-12-07   0.135053
36 2020-12-07    b  0.998768 2020-12-01 2020-12-07        NaN
37 2020-12-08    b  0.955664 2020-12-01 2020-12-07        NaN
38 2020-12-09    b  0.330856 2020-12-01 2020-12-07        NaN
39 2020-12-10    b  0.826502 2020-12-01 2020-12-07        NaN

Другие вопросы по теме