zmc
2023-12-22 9fdbf60165db0400c2e8e6be2dc6e88138ac719a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
cimport cython
from cython cimport Py_ssize_t
from numpy cimport (
    int64_t,
    ndarray,
    uint8_t,
)
 
import numpy as np
 
cimport numpy as cnp
 
cnp.import_array()
 
from pandas._libs.dtypes cimport numeric_object_t
from pandas._libs.lib cimport c_is_list_like
 
 
@cython.wraparound(False)
@cython.boundscheck(False)
def unstack(numeric_object_t[:, :] values, const uint8_t[:] mask,
            Py_ssize_t stride, Py_ssize_t length, Py_ssize_t width,
            numeric_object_t[:, :] new_values, uint8_t[:, :] new_mask) -> None:
    """
    Transform long values to wide new_values.
 
    Parameters
    ----------
    values : typed ndarray
    mask : np.ndarray[bool]
    stride : int
    length : int
    width : int
    new_values : np.ndarray[bool]
        result array
    new_mask : np.ndarray[bool]
        result mask
    """
    cdef:
        Py_ssize_t i, j, w, nulls, s, offset
 
    if numeric_object_t is not object:
        # evaluated at compile-time
        with nogil:
            for i in range(stride):
 
                nulls = 0
                for j in range(length):
 
                    for w in range(width):
 
                        offset = j * width + w
 
                        if mask[offset]:
                            s = i * width + w
                            new_values[j, s] = values[offset - nulls, i]
                            new_mask[j, s] = 1
                        else:
                            nulls += 1
 
    else:
        # object-dtype, identical to above but we cannot use nogil
        for i in range(stride):
 
            nulls = 0
            for j in range(length):
 
                for w in range(width):
 
                    offset = j * width + w
 
                    if mask[offset]:
                        s = i * width + w
                        new_values[j, s] = values[offset - nulls, i]
                        new_mask[j, s] = 1
                    else:
                        nulls += 1
 
 
@cython.wraparound(False)
@cython.boundscheck(False)
def explode(ndarray[object] values):
    """
    transform array list-likes to long form
    preserve non-list entries
 
    Parameters
    ----------
    values : ndarray[object]
 
    Returns
    -------
    ndarray[object]
        result
    ndarray[int64_t]
        counts
    """
    cdef:
        Py_ssize_t i, j, count, n
        object v
        ndarray[object] result
        ndarray[int64_t] counts
 
    # find the resulting len
    n = len(values)
    counts = np.zeros(n, dtype="int64")
    for i in range(n):
        v = values[i]
 
        if c_is_list_like(v, True):
            if len(v):
                counts[i] += len(v)
            else:
                # empty list-like, use a nan marker
                counts[i] += 1
        else:
            counts[i] += 1
 
    result = np.empty(counts.sum(), dtype="object")
    count = 0
    for i in range(n):
        v = values[i]
 
        if c_is_list_like(v, True):
            if len(v):
                v = list(v)
                for j in range(len(v)):
                    result[count] = v[j]
                    count += 1
            else:
                # empty list-like, use a nan marker
                result[count] = np.nan
                count += 1
        else:
            # replace with the existing scalar
            result[count] = v
            count += 1
    return result, counts