Разделить DataFrame на куски - PullRequest
       6

Разделить DataFrame на куски

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

У меня есть DataFrame, который содержит имя, год, тег и кучу других переменных.Таким образом, это может выглядеть так= 1 и

все строки, в которых существуют тег == 0, строка [год + 1] и строка [год-1], строка [[год + -1, «тег»]]] == 1 и строка [[year + -1, "name"]] == row [[year, "name"]].

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

  name  tag        x1        x2  year
0    A    0 -1.352707  0.932559  1999
1    A    1 -1.359828  0.724635  2000
2    A    0  1.289980  0.477135  2001

и

7    B    0 -0.047398  0.307596  2012
8    B    1  1.240108  0.667082  2013
9    B    0  0.558432  0.284363  2014

. Я думал о группировании по нескольким столбцам, но проблема в том, что нужные мне строкигруппа не имеет ничего общего, кроме названия.Я также думал о том, чтобы вручную ввести (в цикле for) другой столбец, который дает каждому чанку новый идентификатор, который я затем мог бы сгруппировать.Однако я очень недоволен этим подходом, поскольку он не кажется ни эффективным, ни элегантным.

Буду признателен за любые идеи.

Ответы [ 2 ]

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

Хотя ответ @ ScottBoston отлично работает с кадром данных, который я дал в этом вопросе, он не работает в тех случаях, когда пропущен год.Так, например, в случае

df = pd.DataFrame({
    "name": 4*["A"] + 6*["B"],
    "year": [1999,2000,2001,2002,2008,2010,2011,2012,2013,2014],
    "tag": [0,1,0,0,0,1,0,0,1,0],
    "x1": np.random.normal(size=10),
    "x2": np.random.uniform(size=10)
})  


print df

  name  tag        x1        x2  year
0    A    0 -0.387840  0.729721  1999
1    A    1 -0.112094  0.813332  2000
2    A    0  0.913186  0.115521  2001
3    A    0 -1.088056  0.983111  2002
4    B    0  0.037521  0.743706  2008
5    B    1  0.602878  0.007256  2010
6    B    0 -0.340498  0.961602  2011
7    B    0  0.170654  0.293789  2012
8    B    1  0.973555  0.942687  2013
9    B    0 -0.643503  0.133091  2014

код будет давать

grp = df.groupby(['name',
                df.tag.cumsum().rolling(3, center=True, min_periods=1).max()])

chunks_df = {}
for n, g in grp:
    if g.shape[0] >= 3:
        chunks_df[n] = g
        print n
        print g, "\n"    


('A', 1.0)
  name  tag        x1        x2  year
0    A    0 -0.387840  0.729721  1999
1    A    1 -0.112094  0.813332  2000
2    A    0  0.913186  0.115521  2001
3    A    0 -1.088056  0.983111  2002 

('B', 2.0)
  name  tag        x1        x2  year
4    B    0  0.037521  0.743706  2008
5    B    1  0.602878  0.007256  2010
6    B    0 -0.340498  0.961602  2011 

('B', 3.0)
  name  tag        x1        x2  year
7    B    0  0.170654  0.293789  2012
8    B    1  0.973555  0.942687  2013
9    B    0 -0.643503  0.133091  2014

, что показывает, что размер первого блока неверен, а второго блока не должно быть в соответствии свторое условие в исходном вопросе (годы 2008, 2010 и 2011).

Двумя проблемами являются

  1. Вопрос явно оставляет открытой возможность для строки находиться вболее одного куска, поэтому одного дополнительного индекса обычно не может быть достаточно.
  2. Необходимо включить условие для лет, поэтому скользящий расчет должен выполняться по двум столбцам (тег и год) одновременно,который в настоящее время не поддерживается пандами в соответствии с https://stackoverflow.com/a/37491779/2336654.

Так что мой обходной путь теперь следующий:

def rolling(df, func, window_size=3):
    dxl = int(window_size/2)    
    if window_size % 2 == 0:
        dxu = dxl
    else:
        dxu = dxl+1
    xmin = dxl
    xmax = len(df)-dxu+1

    for i in xrange(xmin,xmax):
        chunk = df.iloc[i-dxl:i+dxu,:]
        if func(chunk):
            yield chunk



def valid(chunk):
    if len(chunk.name.value_counts()) != 1:
        return False
    if chunk.tag.iloc[1] != 1:
        return False
    if chunk.year.iloc[2]-chunk.year.iloc[0] != 2:
        return False
    return True



new_df = pd.DataFrame()
for ichunk, chunk in enumerate(rolling(df, window_size=3, func=valid)):
    new_df = new_df.append(chunk.assign(new_tag=ichunk), ignore_index=True)

for name, g in new_df.groupby(["name","new_tag"]):
    print name
    print g,"\n"


('A', 0)
  name  tag        x1        x2  year  new_tag
0    A    0 -1.046241  0.692206  1999        0
1    A    1  0.373060  0.919130  2000        0
2    A    0  1.316474  0.463517  2001        0 

('B', 1)
  name  tag        x1        x2  year  new_tag
3    B    0  0.376408  0.743188  2012        1
4    B    1  0.019062  0.647851  2013        1
5    B    0 -0.442368  0.506169  2014        1 

Просто подумал, что я должен добавить это на тот случай, если кто-то в будущем будет удивлятьсяпочему принятый ответ не работает для аналогичной проблемы.

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

Давайте попробуем немного логики:

df = pd.DataFrame({
    "name": 4*["A"] + 5*["B"],
    "year": [1999,2000,2001,2002,2010,2011,2012,2013,2014],
    "tag": [0,1,0,0,1,0,0,1,0],
    "x1": np.random.normal(size=9),
    "x2": np.random.uniform(size=9)
})

grp = df.groupby(['name',
                df.tag.cumsum().rolling(3, center=True, min_periods=1).max()])

chunks_df = {}
for n, g in grp:
    if g.shape[0] >= 3:
        chunks_df[n] = g

Где chunks_df - это словарь вашего разбитого фрейма данных:

chunks_df[('A', 1.0)]

  name  year  tag        x1        x2
0    A  1999    0 -0.015852  0.553314
1    A  2000    1  0.367290  0.245546
2    A  2001    0  0.605592  0.524358

chunks_df[('B', 3.0)]

  name  year  tag        x1        x2
6    B  2012    0 -0.750010  0.432032
7    B  2013    1 -0.682009  0.971042
8    B  2014    0  1.066113  0.179048

Подробности:

  • Используйте cumsum для уникальной идентификации / маркировки каждого тега == 1.
  • Используйте прокрутку с окном 3 и получите максимум этого центрированного окна, чтобы выбрать -1, 1 и + 1.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...