Применять арифметические вычисления к определенным строкам большого кадра данных. - PullRequest
0 голосов
/ 25 апреля 2019

Предположим, что у нас есть фрейм данных (df) с большим количеством строк (1600000X4). Кроме того, у нас есть список списков, таких как этот:

inx = [[1,2],[4,5], [8,9,10], [15,16]]

Нам нужно вычислить среднее для первого и третьего столбцов этого фрейма данных и медиану для второго и четвертого столбцов для каждого списка в inx. Например, для первого списка inx мы должны сделать это для первой и второй строк и заменить все эти строки новой строкой, которая содержит выходные данные этих вычислений. Какой самый быстрый способ сделать это?

import numpy as np
import pandas as pd

df = pd.DataFrame(np.array([[1, 2, 3, 3], [4, 5, 6, 1], [7, 8, 9, 3], [1, 1, 1, 1]]), columns=['a', 'b', 'c', 'd'])

   a  b  c  d
0  1  2  3  3
1  4  5  6  1
2  7  8  9  3
3  1  1  1  1    

Вывод только для первого списка внутри inx ([1,2]) будет выглядеть примерно так:

   a  b  c  d
0  1  2  3  3
1  5.5  6.5  7.5  2
3  1  1  1  1   

Как видите, мы не меняем первую строку (0), потому что ее нет в основном списке. После этого мы собираемся сделать то же самое для [4,5]. Мы ничего не меняем в строке 3, потому что ее тоже нет в списке. inx - это большой список списков (более 100000 элементов).

1 Ответ

1 голос
/ 26 апреля 2019

РЕДАКТИРОВАТЬ: НОВЫЙ ПОДХОД, ИЗБЕГАЮЩИЙ ЦИКЛОВ

Здесь ниже вы найдете подход, основанный на пандах и избегающий циклов.

После генерации некоторых поддельных данных с тем же размеромваш, я в основном создаю список индексов из вашего списка строк inx;т. е. с вашим inx:

[[2,3], [5,6,7], [10,11], ...]

созданный список:

[[1,1], [2,2,2], [3,3],...]

После этого этот список сглаживается и добавляется в исходный кадр данных, чтобы пометить различные группы строк длядействуют на.После правильных вычислений результирующий кадр данных объединяется с исходными строками, которые не требуют вычислений (в моем примере выше, строки: [0, 1, 4, 8, 9, ...]).Вы найдете больше комментариев в коде.

В конце ответа я также оставляю свой предыдущий подход к записям.На моем ящике старый алгоритм, включающий цикл, занимает более 18 минут ... невыносимо!Использование только панд, это занимает менее половины секунды!Панды великолепны!

import pandas as pd
import numpy as np
import random

# Prepare some fake data to test
data = np.random.randint(0, 9, size=(160000, 4))
df = pd.DataFrame(data, columns=['a', 'b', 'c', 'd'])

inxl = random.sample(range(1, 160000), 140000)
inxl.sort()

inx=[]
while len(inxl) > 3:
    i = random.randint(2,3)
    l = inxl[0:i]
    inx.append(l)
    inxl = inxl[i:]
inx.append(inxl)



# flatten inx (used below)
flat_inx = [item for sublist in inx for item in sublist]
# for each element (list) in inx create equivalent list (same length)
# of increasing ints. They'll be used to group corresponding rows
gr=[len(sublist) for sublist in inx]
t = list(zip(gr, range(1, len(inx)+1)))

group_list = [a*[b] for (a,b) in t]

# the groups are flatten either
flat_group_list = [item for sublist in group_list for item in sublist]

# create a new dataframe to mark rows to group retaining 
# original index for each row
df_groups = pd.DataFrame({'groups': flat_group_list}, index=flat_inx)
# and join the group dataframe to the original df
df['groups'] = df_groups
# rows not belonging to a group are marked with 0
df['groups']=df['groups'].fillna(0)

# save rows not belonging to a group for later
df_untouched = df[df['groups'] == 0]
df_untouched = df_untouched.drop('groups', axis=1)

# new dataframe containg only rows belonging to a group
df_to_operate = df[df['groups']>0]
df_to_operate = df_to_operate.assign(ind=df_to_operate.index)

# at last, we group the rows according to original inx
df_grouped = df_to_operate.groupby('groups')

# calculate mean and median
# for each group we retain the index of first row of group
df_operated =df_grouped.agg({'a' : 'mean',
                             'b' : 'median',
                             'c' : 'mean',
                             'd' : 'median',
                             'ind': 'first'})

# set correct index on dataframe
df_operated=df_operated.set_index('ind')

# finally, join the previous dataframe with saved
# dataframe of rows which don't need calcullations
df_final = df_operated.combine_first(df_untouched)

СТАРЫЙ АЛГО, СЛИШКОМ МЕДЛЕННЫЙ ДЛЯ ТАКИХ ДАННЫХ

Этот алгоритм, включающий цикл, хотя и дает правильный результат, занимает много времени длятакой большой объем данных:

import pandas as pd

df = pd.DataFrame(np.array([[1, 2, 3, 3], [4, 5, 6, 1], [7, 8, 9, 3], [1, 1, 1, 1]]), columns=['a', 'b', 'c', 'd'])

inx = [[1,2]]

for l in inx:
    means=df.iloc[l][['a', 'c']].mean()
    medians=df.iloc[l][['b', 'd']].median()
    df.iloc[l[0]]=pd.DataFrame([means, medians]).fillna(method='bfill').iloc[0]
    df.drop(index=l[1:], inplace=True)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...