Как я могу сделать мой цикл намного быстрее / лучше для этой конкретной проблемы? - PullRequest
0 голосов
/ 07 ноября 2018

Python начинающий здесь.

Вот моя проблема: У меня есть CSV-файл с примерно 3200 строк и 660 столбцов. Строки заполнены 0, 1 или 50 с.

Мне нужно обновить только что созданный столбец 'answer' согласно этим требованиям:

  1. Это должна быть сумма 1 с в этой строке, которая должна быть до того, как произойдет '50'.
  2. Если в этой строке нет '50', просто обновите последний столбец до нуля.

так, например, строка [1, 0, 0, 0, 1, 1, 50, 0, 0, 0, 1] должна иметь новое значение в конце как «3», потому что мы нашли три 1, прежде чем найти 50.

Вот мой код:

df_numRows = len(df.values)
df_numCols = len(df.columns)

for row in range(df_numRows):
    df_sum = 0
    for col in range(df_numCols):
        if '50' not in df.values[row]:
            df.at[row, 'answer'] = '0'
        elif df.values[row][col] == '0':
            continue
        elif df.values[row][col] == '1':
            df_sum += 1
            df.at[row, 'answer'] = df_sum
        elif df.values[row][col] == '50':
            break

Я написал этот вложенный цикл for для итерации по моему фрейму данных Pandas, но, похоже, для его запуска требуется ОЧЕНЬ много времени.

Я запустил этот фрагмент кода в том же наборе данных, но только с 100 строками x 660 столбцами, и это заняло около 1,5 минут, однако, когда я пытаюсь запустить его на всем объекте, он работал около 2,5 часов, и я просто закрыл его, потому что я думал, что это заняло слишком много времени.

Как я могу сделать мой код более эффективным / быстрее / лучше? Ребята, я бы очень хотел получить любую помощь, и заранее прошу прощения, если это простой вопрос, но я только начинаю работать с Python!

Спасибо, ребята!

Ответы [ 4 ]

0 голосов
/ 07 ноября 2018

Это решает проблему, хотя и немного устойчиво:

import pandas as pd
import numpy as np

np.random.seed(1)

df = pd.DataFrame(np.random.choice([0, 1, 50], (3200,660)))

data = df.values
idxs = [np.where(d == 50) for d in data]
sums = [sum(d[:i[0][0]]) if i[0].size else 0 for d, i in zip(data, idxs)]

data = np.column_stack((data, sums))

df = df.assign(answer=sums)

df.head()

#    0   1   2   3   4   5  6   7   8   9   ...    651  652  653  654  655  \
#0   1   0   0   1   1   0  0   1   0   1   ...     50   50    1    1    0   
#1   1   0  50   1  50  50  0   1   1  50   ...      1    0    1    0    0   
#2  50   0   1   0   1  50  1  50   0  50   ...      0   50    1   50   50   
#3   0   1   0  50   1   0  0  50   1   0   ...      1    1    0    1    1   
#4   1  50   1   1   1   1  0  50  50   1   ...      0    1    0    1    0   
#
#   656  657  658  659  answer  
#0    0    0    1    0       5  
#1    1   50    0   50       1  
#2   50    1    1   50       0  
#3    0   50    1   50       1  
#4    0   50    0   50       1  
0 голосов
/ 07 ноября 2018

Настройка

df = pd.DataFrame([
    [1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1],    # No 50s
    [1, 0, 0, 0, 1, 1, 50, 0, 0, 0, 1],   # One 50
    [1, 50, 0, 0, 1, 50, 50, 0, 0, 0, 1], # Three 50s but 2 are consecutive
    [1, 50, 0, 0, 1, 1, 50, 0, 0, 0, 1],  # Two 50s
])

df

   0   1   2   3   4   5   6   7   8   9   10
0   1   0   0   0   1   1   0   0   0   0   1
1   1   0   0   0   1   1  50   0   0   0   1
2   1  50   0   0   1  50  50   0   0   0   1
3   1  50   0   0   1   1  50   0   0   0   1

Используйте logical_and с его accumulate методом

np.logical_and возьмет оператор and и применит его к группе логических значений. Часть accumulate говорит о том, чтобы продолжать применять ее, и по мере отслеживания самой последней and всех предыдущих логических величин. Указав axis=1, я говорю сделать это для каждой строки. Это возвращает массив логических значений, где строки истинны, пока мы не достигнем значения 50. Затем я проверяю, есть ли пятьдесят с all(1). Правильное умножение дает суммы всех значений, а не 50 до первых 50 ... для каждой строки.

d = np.logical_and.accumulate(df.ne(50), axis=1)

df.mul(d).mul(~d.all(1), 0).sum(1)

0    0
1    3
2    1
3    1
dtype: int64

Объединить, чтобы получить новый столбец

d = np.logical_and.accumulate(df.ne(50), axis=1)

df.assign(answer=df.mul(d).mul(~d.all(1), 0).sum(1))

   0   1  2  3  4   5   6  7  8  9  10  asnswer
0  1   0  0  0  1   1   0  0  0  0   1        0
1  1   0  0  0  1   1  50  0  0  0   1        3
2  1  50  0  0  1  50  50  0  0  0   1        1
3  1  50  0  0  1   1  50  0  0  0   1        1

Если ты хочешь быть полностью раздутым Numpy

v = df.values
a = np.logical_and.accumulate(v != 50, axis=1)
df.assign(answer=(v * (a & ~a.all(1, keepdims=True))).sum(1))

   0   1  2  3  4   5   6  7  8  9  10  asnswer
0  1   0  0  0  1   1   0  0  0  0   1        0
1  1   0  0  0  1   1  50  0  0  0   1        3
2  1  50  0  0  1  50  50  0  0  0   1        1
3  1  50  0  0  1   1  50  0  0  0   1        1
0 голосов
/ 07 ноября 2018

Пожалуйста, попробуйте эту логику и дайте мне знать, если это поможет.

df_numRows = len(df.values)
df_numCols = len(df.columns)

for row in range(df_numRows):
    df_sum = 0

    try:
        indexOf50 = np.argwhere(df.loc[row]==50)[0][0]
        colArrayTill50 = df.loc[row][:indexOf50].values
        numberOfOne = colArrayTill50.sum()
    except:
        numberOfOne = 0

    print(numberOfOne)
0 голосов
/ 07 ноября 2018

Просто сделайте cumprod после того, как мы найдем 50, если это 50, то все значения, приведенные ниже, станут 0, тогда мы с помощью этого логического фрейма данных фильтруем исходный df и делаем sum

df=pd.DataFrame({'A':[1, 0, 0, 0, 1, 1, 50, 0, 0, 0, 1] })
df.mul(df.ne(50).cumprod()).sum()
Out[35]: 
A    3
dtype: int64
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...