Как получить ближайшее деление на число 100 в питоне панды - PullRequest
0 голосов
/ 08 декабря 2018

Я хотел бы добавить новый столбец в кадре данных pandas на основе входного столбца.Вновь добавленный столбец должен быть заполнен следующим образом.

  1. первая строка должна быть заполнена с ближайшим делением на число 100.
  2. , начиная со следующей строки, вывод будет повторяться до тех пор, пока его разностьс входным значением больше или равно 100.

    input       output
    11700.15    11700
    11695.20    11700
    11661.00    11700
    11630.40    11700
    11666.10    11700
    11600.30    11700
    11600.00    11600
    11555.40    11600
    11655.20    11600
    11699.00    11600
    11701.55    11700
    11799.44    11700
    11604.65    11700
    11600.33    11700
    11599.65    11600
    

Какой самый элегантный способ сделать это в пандах?

Ответы [ 3 ]

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

Не элегантно ни в коем случае, но тогда я думаю, что нет никакого способа обойти цикл для этого (может быть неправильно!):

vals = df1['input'].values
anchor = vals[0]
ch = np.zeros(len(vals))
ch.fill(np.nan)
for i in range(len(vals)):
    if abs(vals[i] - anchor) >= 100:
        anchor = vals[i]
        ch[i] = 1
    else:
        continue
ch[0] = 1

df['out_check'] = pd.Series(100* np.round((df['input'] * ch)/100)).ffill()

ВЫХОД:

       input  output  out_check
0   11700.15   11700    11700.0
1   11695.20   11700    11700.0
2   11661.00   11700    11700.0
3   11630.40   11700    11700.0
4   11666.10   11700    11700.0
5   11600.30   11700    11700.0
6   11600.00   11600    11600.0
7   11555.40   11600    11600.0
8   11655.20   11600    11600.0
9   11699.00   11600    11600.0
10  11701.55   11700    11700.0
11  11799.44   11700    11700.0
12  11604.65   11700    11700.0
13  11600.33   11700    11600.0
14  11599.65   11600    11600.0

Я делаюсчитаю, что последние два значения в output должны быть 1600.

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

Решение, которое я придумал:

last = df.loc[0, 'input'].round(-2)
for ix in range(len(df)):
    inp = df.loc[ix, 'input']
    last = inp.round(-2) if abs(inp - last) >= 100 else last
    df.loc[ix, 'output'] = last

, которое дает именно тот результат, который дает OP.

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

Насколько я могу судить, здесь нет интуитивного подхода, который бы не включал явную итерацию, что не идеально для numpy и pandas.Однако временная сложность этой проблемы - O (n), что делает ее хорошей целью для библиотеки numba.Это позволяет нам найти очень эффективное решение.

Одна заметка о моем решении, я округляю с помощью (a + threshold // 2) // threshold * threshold, что выглядит многословно по сравнению с использованием np.round(a, decimals=-2).Это связано с природой использования флага numba nopython=True, который не совместим с функцией np.round.


from numba import jit

@jit(nopython=True)
def cumsum_with_threshold(arr, threshold):
       """
       Rounds values in an array, propogating the last value seen until
       a cumulative sum reaches a threshold
       :param arr: the array to round and sum
       :param threshold: the point at which to stop propogation
       :return: rounded output array
       """

       s = a.shape[0]
       o = np.empty(s)
       d = a[0]
       r = (a + threshold // 2) // threshold * threshold
       c = 0
       o[0] = r[0]

       for i in range(1, s):
           if np.abs(a[i] - d) > threshold:
               o[i] = r[i]
               d = a[i]
           else:
               o[i] = o[i - 1]

       return o

Давайте проверим это:

a = df['input'].values
pd.Series(cumsum_with_threshold(a, 100))

0     11700.0
1     11700.0
2     11700.0
3     11700.0
4     11700.0
5     11700.0
6     11600.0
7     11600.0
8     11600.0
9     11600.0
10    11700.0
11    11700.0
12    11700.0
13    11600.0
14    11600.0
dtype: float64

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

for i in range(1, s):
   if np.abs(a[i] - d) > t:
       o[i] = r[i]
       # OLD d = a[i]
       d = r[i]
   else:
       o[i] = o[i - 1]

Чтобы проверить эффективность, давайте запустим это для гораздо большего набора данных:

l = np.random.choice(df['input'].values, 10_000_000)

%timeit cumsum_with_threshold(l, 100)
1.54 µs ± 7.93 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...