Подсчитайте количество высших строк для каждой строки панд - PullRequest
0 голосов
/ 25 октября 2018

У меня есть 2 столбца DataFrame:

positions = pd.DataFrame({"pos" : [1, 2, 3, 4, 5], "mcap" : [1, 4, 3, 2, 5]}, index = ["a", "b", "c", "d", "e"])

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

Таким образом, ответ для примера выше будет:

pd.Series([4, 1, 1, 1, 0], index = ["a", "b", "c", "d", "e"])

Я знаю, как сделать это в цикле, но это занимает много вычислительного времени, как только DataFrame становится большим, поэтому я ищудля более питонического способа сделать это.

РЕДАКТИРОВАТЬ.простое решение по циклу.

answer = pd.Series(np.zeros(len(positions)), index = ["a", "b", "c", "d", "e"])
for asset in ["a", "b", "c", "d", "e"]:
    better_by_signal = positions[positions["pos"] > positions["pos"].loc[asset]].index
    better_by_cap = positions[positions["mcap"] > positions["mcap"].loc[asset]].index
    idx_intersection = better_by_signal.intersection(better_by_cap)
    answer[asset] = len(idx_intersection)

Ответы [ 4 ]

0 голосов
/ 25 октября 2018

Вы можете использовать numpy вещание, чтобы найти все пары положительных разностей для оси x (pos) и оси y (mcap):

import numpy as np
import pandas as pd

positions = pd.DataFrame({"pos" : [1, 2, 3, 4, 5], "mcap" : [1, 4, 3, 2, 5]}, index = ["a", "b", "c", "d", "e"])

arrx = np.asarray([positions.pos])
arry = np.asarray([positions.mcap])
positions["count"] = ((arrx - arrx.T > 0) & (arry - arry.T > 0)).sum(axis = 1)

print(positions)

Пример вывода

   pos  mcap  count
a    1     1      4
b    2     4      1
c    3     3      1
d    4     2      1
e    5     5      0
0 голосов
/ 25 октября 2018

Вы можете вместо цикла for использовать понимание списка следующим образом:

import pandas as pd
import numpy as np


positions = pd.DataFrame({"pos": [1, 2, 3, 4, 5], 
                          "mcap": [1, 4, 3, 2, 5]}, 
                         index=["a", "b", "c", "d", "e"]) 

# gives you a list:
answer = [sum(np.sum((positions - positions.iloc[i] > 0).values, axis=1) ==
              2) for i in range(len(positions))]

# convert list to a `pd.Series`:
answer = pd.Series(answer, index=positions.index)
0 голосов
/ 25 октября 2018

Вы можете использовать свертки.Convolution делает что-то вроде этого (более подробная информация здесь ):

enter image description here

Он будет проходить через матрицу, умножающую ваш фильтр или пэд наэлементы матрицы, а затем их сложение в этом случае.

Для этого вопроса давайте сначала добавим новый элемент f в кадр данных, чтобы хотя бы в одной строке было более одного элемента.

>> positions

   pos  mcap
a    1     1
b    2     4
c    3     3
d    4     2
e    5     5
f    3     2

Позиции также можно увидеть как:

df = pd.crosstab(positions['pos'], positions['mcap'], 
                 values=positions.index, aggfunc=sum)

df

mcap    1    2    3    4    5
pos                          
1       a  NaN  NaN  NaN  NaN
2     NaN  NaN  NaN    b  NaN
3     NaN    f    c  NaN  NaN
4     NaN    d  NaN  NaN  NaN
5     NaN  NaN  NaN  NaN    e


df_ones = df.notnull() * 1

mcap  1  2  3  4  5
pos                
1     1  0  0  0  0
2     0  0  0  1  0
3     0  1  1  0  0
4     0  1  0  0  0
5     0  0  0  0  1

Мы можем создать окно, которое скользит по df_ones и суммировать количество элементов, которые попадают под окно.Это называется «сверткой» (или корреляцией).

Теперь давайте создадим окно, в котором отсутствует верхний левый угловой элемент (поэтому он не учитывается), и свернем его с нашим df_ones, чтобы получить результат:

pad = np.ones_like(df.values)
pad[0, 0] = 0

pad

array([[0, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]], dtype=object)


counts = ((signal.correlate(df_ones.values, pad,
                            mode='full')[-df.shape[0]:,
                                         -df.shape[1]:]) * \ 
          df_ones).unstack().replace(0, np.nan).dropna(
          ).reset_index().rename(columns={0: 'count'})

   mcap  pos  count
0     1    1    5.0
1     2    3    3.0
2     2    4    1.0
3     3    3    1.0
4     4    2    1.0

positions.reset_index().merge(counts, 
                              how='left').fillna(0
     ).sort_values('pos').set_index('index')

       pos  mcap  count
index                  
a        1     1    5.0
b        2     4    1.0
c        3     3    1.0
f        3     2    3.0
d        4     2    1.0
e        5     5    0.0

Все в функции:

def count_upper(df):
    df = pd.crosstab(positions['pos'], positions['mcap'],
                     values=positions.index, aggfunc=sum)
    df_ones = df.notnull() * 1

    pad = np.ones_like(df.values)
    pad[0, 0] = 0

    counts = ((signal.correlate(df_ones.values, pad,
                                mode='full')[-df.shape[0]:,
                                             -df.shape[1]:]) * df_ones)
    counts = counts.unstack().replace(0, np.nan).dropna(
    ).reset_index().rename(columns={0: 'count'})

    result = positions.reset_index().merge(counts,
                                         how='left')
    result = result.fillna(0).sort_values('pos').set_index('index')
    return result

Для вашего примера результат будет соответствовать ожидаемому результату:

positions = pd.DataFrame({"pos" : [1, 2, 3, 4, 5],
                          "mcap" : [1, 4, 3, 2, 5]},
                         index = ["a", "b", "c", "d", "e"])
>> count_upper(positions)
       pos  mcap  count
index                  
a        1     1    4.0
b        2     4    1.0
c        3     3    1.0
d        4     2    1.0
e        5     5    0.0
0 голосов
/ 25 октября 2018

Используйте карту вместо циклического перемещения по индексу, это должно работать: -

  import pandas as pd
  import numpy as np

  positions = pd.DataFrame({"pos" : [1, 2, 3, 4, 5], "mcap" : [1, 4, 3, 2, 5]}, index = ["a", "b", "c", "d", "e"])
  answer = pd.Series(np.zeros(len(positions)), index = ["a", "b", "c", "d", "e"])

  def set_pos(asset):
     better_by_signal = positions[positions["pos"] > positions["pos"].loc[asset]].index
     better_by_cap = positions[positions["mcap"] > positions["mcap"].loc[asset]].index
     idx_intersection = better_by_signal.intersection(better_by_cap)
     return len(idx_intersection)

  len_intersection = map(set_pos, answer.index.tolist())
  final_answer = pd.Series(len_intersection, index = answer.index.tolist())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...