向左步进行值,直到在Python中不为空

时间:2018-10-16 16:24:54

标签: python pandas performance numpy dataframe

我必须处理大量数据。每行以1或0开头。我需要一个数据行,其中每行以1开头,因此我必须将所有行的值左移,直到第一个值为1。

例如:

0 1 0 0 1 0 0
1 0 0 0 0 1 1
0 0 0 1 0 0 1
0 0 0 0 0 1 1

结果必须是这样的:

1 0 0 1 0 0 0
1 0 0 0 0 1 1
1 0 0 1 0 0 0
1 1 0 0 0 0 0

我不想用于,等,因为我需要使用pandas或numpy的更快的方法。

您对这个问题有想法吗?

4 个答案:

答案 0 :(得分:4)

您可以将cummaxNaNsorted一起使用以掩盖所有需要平移的位置

df[df.cummax(1).ne(0)].apply(lambda x : sorted(x,key=pd.isnull),1).fillna(0).astype(int)
Out[310]: 
   1  2  3  4  5  6  7
0  1  0  0  1  0  0  0
1  1  0  0  0  0  1  1
2  1  0  0  1  0  0  0
3  1  1  0  0  0  0  0

或者我们使用由 Divakar 编写的函数justify(比应用排序快得多)

pd.DataFrame(justify(df[df.cummax(1).ne(0)].values, invalid_val=np.nan, axis=1, side='left')).fillna(0).astype(int)
Out[314]: 
   0  1  2  3  4  5  6
0  1  0  0  1  0  0  0
1  1  0  0  0  0  1  1
2  1  0  0  1  0  0  0
3  1  1  0  0  0  0  0

答案 1 :(得分:3)

您可以在此处使用numpy.ogrid

foreach($json['included'] as $key => $title) {
    $cusa = substr(explode('-', $title['id'], 3)[1], 0, -3);

    if($title['type'] == 'game' && substr($cusa, 0, 4) == 'CUSA') {
        // if the day of release has already passed, skip
        if (strtotime(date('Y-m-d H:i:s')) > strtotime($title['attributes']['release-date'])) continue;
            ?>
            <div class="game-banner" style="background:url(<?php echo $title['attributes']['thumbnail-url-base']; ?>)">
                <h4 class="psplus-game-name"><?php echo $title['attributes']['name']; ?></h4>
            </div>
            <?php
            if($key >= 4) break; // display only 3
        }
    }
}

a = df.values
s = a.argmax(1) * - 1
m, n = a.shape
r, c = np.ogrid[:m, :n]
s[s < 0] += n
c = c - s[:, None]
a[r, c]

时间

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

答案 2 :(得分:2)

为了提高性能,您可以使用numba。一个基本循环,但是在JIT编译和在C级别使用更多基本对象的情况下有效:

from numba import njit

@njit
def shifter(A):
    res = np.zeros(A.shape)
    for i in range(res.shape[0]):
        start, end = 0, 0
        for j in range(res.shape[1]):
            if A[i, j] != 0:
                start = j
                break
        res[i, :res.shape[1]-start] = A[i, start:]
    return res

性能基准化

def jpp(df):
    return pd.DataFrame(shifter(df.values).astype(int))

def user348(df):
    a = df.values
    s = a.argmax(1) * - 1
    m, n = a.shape
    r, c = np.ogrid[:m, :n]
    s[s < 0] += n
    c = c - s[:, None]
    return pd.DataFrame(a[r, c])    

np.random.seed(0)
df = pd.DataFrame(np.random.randint(0, 2, (1000, 1000)))

assert np.array_equal(jpp(df).values, user348(df).values)

%timeit jpp(df)      # 9.2 ms per loop
%timeit user348(df)  # 18.5 ms per loop

答案 3 :(得分:2)

这是一个stride_tricks解决方案,它之所以快速,是因为它支持逐片复制。

def pp(x):
    n, m = x.shape
    am = x.argmax(-1)
    mam = am.max()
    xx = np.empty((n, m + mam), x.dtype)
    xx[:, :m] = x
    xx[:, m:] = 0
    xx = np.lib.stride_tricks.as_strided(xx, (n, mam+1, m), (*xx.strides, xx.strides[-1]))
    return xx[np.arange(x.shape[0]), am]

它用所需的零填充空白,然后使用as_strided创建滑动窗口视图。使用花式索引可以解决此问题,但是由于最后一个维度没有索引,因此行的复制得到了优化和快速。

多快?对于与numba相等的足够大的输入:

x = np.random.randint(0, 2, (10000, 10))

from timeit import timeit

shifter(x) # that should compile it, right?

print(timeit(lambda:shifter(x).astype(x.dtype), number=1000))
print(timeit(lambda:pp(x), number=1000))

示例输出:

0.8630472810036736
0.7336142909916816