2017-02-22 36 views
3

Вот мой вклад:панды заменить только часть колонки

import pandas as pd 
import numpy as np 

list1 = [10,79,6,38,4,557,12,220,46,22,45,22] 
list2 = [4,3,23,6,234,47,312,2,426,42,435,23] 

df = pd.DataFrame({'A' : list1, 'B' : list2}, columns = ['A', 'B']) 
df['C'] = np.where (df['A'] > df['B'].shift(-2), 1, np.nan) 
print (df) 

, который производит этот выход:

 A B C 
0 10 4 NaN 
1 79 3 1.0 
2  6 23 NaN 
3 38 6 NaN 
4  4 234 NaN 
5 557 47 1.0 
6 12 312 NaN 
7 220 2 1.0 
8 46 426 NaN 
9 22 42 NaN 
10 45 435 NaN 
11 22 23 NaN 

Что мне нужно сделать, это изменить столбец «C», чтобы быть набор из трех 1 в ряд, неперекрывающихся. Требуемый выход:

 A B C 
0 10 4 NaN 
1 79 3 1.0 
2  6 23 1.0 
3 38 6 1.0 
4  4 234 NaN 
5 557 47 1.0 
6 12 312 1.0 
7 220 2 1.0 
8 46 426 NaN 
9 22 42 NaN 
10 45 435 NaN 
11 22 23 NaN 

Итак, строки 2, 3 и 6 изменяются от NaN до 1.0. Строка 7 уже имеет 1.0 и игнорируется. Строки 8 и 9 должны оставаться NaN, потому что строка 7 является последней записью предыдущего набора.

Я не знаю, есть ли лучший способ построить столбец «C», который будет делать это при создании.

Я пробовал несколько вариантов fillna и ffill, ни один из них не работал для меня.

Это кажется очень запутанным, но я попытался выделить идентификатор строки для каждого 1,0 с этой линией:

print (df.loc[df['C'] == 1]) 

Какие правильно выводит этот:

 A B C 
1 79 3 1.0 
5 557 47 1.0 
7 220 2 1.0 

Даже если я знаю, что информация, я не знаю, как исходить оттуда.

Большое вам спасибо за вашу помощь заранее, Давида

+0

Что делать, если у вас есть последовательность, как '1, NaN, 1,1, NaN, NaN'? что бы вы хотели, чтобы результат был? – Psidom

+0

спасибо за просмотр psidom - результат будет все 1's –

+0

OK. Так должен ли индекс 8 и 9 быть наном в вашем случае? – Psidom

ответ

4

EDIT:

Faster версия (благодаря b2002):

ii = df[pd.notnull(df.C)].index 
dd = np.diff(ii) 
jj = [ii[i] for i in range(1,len(ii)) if dd[i-1] > 2] 
jj = [ii[0]] + jj 

for ci in jj: 
    df.C.values[ci:ci+3] = 1.0 

Сначала получите индексы всех исходных точек, то есть все точки, которые являются 1,0 и имеют два NaN следующие , посмотрев различия между точками, которые не равны нулю в столбце C (первый индекс включен по умолчанию), затем перебирайте эти индексы и используйте loc для изменения фрагментов вашего столбца C:

ii = df[pd.notnull(df.C)].index 
dd = np.diff(ii) 
jj = [ii[i] for i in range(1,len(ii)) if dd[i-1] > 2] 
jj = [ii[0]] + jj 

for ci in jj: 
    df.loc[ci:ci+2,'C'] = 1.0 

Результат:

 A B C 
0 10 4 NaN 
1 79 3 1.0 
2  6 23 1.0 
3 38 6 1.0 
4  4 234 NaN 
5 557 47 1.0 
6 12 312 1.0 
7 220 2 1.0 
8 46 426 NaN 
9 22 42 NaN 
10 45 435 NaN 
11 22 23 NaN 
+0

очень хороший ответ! Действительно, как numpy diff для индексов. – b2002

+0

после некоторого времени, это может быть значительно увеличено для больших кадров данных, если их назначение выполняется с помощью массива numpy df.C, а не с использованием df.loc, который оказывается очень медленным. – b2002

+0

Хрис - Большое спасибо. ваше решение отлично работало на одном и том же df –

1
list1 = [10,79,6,38,4,557,12,220,46,22,45,22] 
list2 = [4,3,23,6,234,47,312,2,426,42,435,23] 

df = pd.DataFrame({'A' : list1, 'B' : list2}, columns = ['A', 'B']) 
df['C'] = np.where (df['A'] > df['B'].shift(-2), 1, np.nan) 

     A B C 
0 10 4 NaN 
1 79 3 1.0 
2  6 23 NaN 
3 38 6 NaN 
4  4 234 NaN 
5 557 47 1.0 
6 12 312 NaN 
7 220 2 1.0 
8 46 426 NaN 
9 22 42 NaN 
10 45 435 NaN 
11 22 23 NaN 

сделать массив из последовательности:

a = np.array(df.C) 

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

def fill_segments(a, test_patterns, fill_patterns): 
    # replace nans with zeros so fast numpy array_equal will work 
    nan_idx = np.where(np.isnan(a))[0] 
    np.put(a, nan_idx, 0.) 
    col_index = list(np.arange(a.size)) 
    # loop forward through sequence comparing segment patterns 
    for j in np.arange(len(test_patterns)): 
     this_pattern = test_patterns[j] 
     snip = len(this_pattern) 
     rng = col_index[:-snip + 1] 
     for i in rng: 
      seg = a[col_index[i: i + snip]] 
      if np.array_equal(seg, this_pattern): 
       # when a match is found, replace values in array segment 
       # with fill pattern 
       pattern_indexes = col_index[i: i + snip] 
       np.put(a, pattern_indexes, fill_patterns[j]) 
    # convert all fillers to ones 
    np.put(a, np.where(a > 1.)[0], 1.) 
    # convert zeros back to nans 
    np.put(a, np.where(a == 0.)[0], np.nan) 

    return a 

Шаблоны должны быть заменены:

p1 = [1., 1., 1.] 
p2 = [1., 0., 1.] 
p3 = [1., 1., 0.] 
p4 = [1., 0., 0.] 

И соответствующие штриховки:

f1 = [5., 5., 5.] 
f2 = [4., 4., 4.] 
f3 = [3., 3., 3.] 
f4 = [2., 2., 2.] 

делают test_patterns и fill_patterns входы

patterns = [p1, p2, p3, p4] 
fills = [f1, f2, f3, f4] 

функцию запуска:

a = fill_segments(a, patterns, fills) 

назначить на колонке C

df.C = a 

ДФ:

 A B C 
0 10 4 NaN 
1 79 3 1.0 
2  6 23 1.0 
3 38 6 1.0 
4  4 234 NaN 
5 557 47 1.0 
6 12 312 1.0 
7 220 2 1.0 
8 46 426 NaN 
9 22 42 NaN 
10 45 435 NaN 
11 22 23 NaN 

Узоры и заливок, возможно, должны быть скорректированы/добавлены в зависимости от того, как столбец ввода изначально заселенной и конкретные правила последовательности результатов.

+0

b2002 - Большое спасибо за ваше решение. он отлично работал на образце df –

+0

b2002 и Khris - спасибо за ваши решения, они оба работали. Я испортил, когда я создал пример выше, потому что на самом деле я пытался использовать его с индексом datetime, а не целочисленным индексом. К сожалению, решения, похоже, не работают с этим dtype. Я разместил здесь следующий вопрос, если вы хотите посмотреть на него. Благодарю. –

+0

http://stackoverflow.com/questions/42418035/pandas-replace-only-part-of-a-column-with-datetime-index –

 Смежные вопросы

  • Нет связанных вопросов^_^