用 numpy 构建一个基本的立方体?

Dav*_*542 18 python olap numpy multidimensional-array olap-cube

我想知道是否numpy可以用于构建最基本的多维数据集模型,其中存储所有交叉组合及其计算值。

让我们以以下数据为例:

AUTHOR         BOOK          YEAR        SALES
Shakespeare    Hamlet        2000        104.2
Shakespeare    Hamlet        2001        99.0
Shakespeare    Romeo         2000        27.0
Shakespeare    Romeo         2001        19.0
Dante          Inferno       2000        11.6
Dante          Inferno       2001        12.6
Run Code Online (Sandbox Code Playgroud)

并且能够构建类似的东西:

                             YEAR                  TOTAL
AUTHOR            BOOK       2000       2001         
(ALL)             (ALL)      142.8      130.6      273.4
Shakespeare       (ALL)      131.2      118.0      249.2
Dante             (ALL)      11.6       12.6       24.2
Shakespeare       Hamlet     104.2      99.0       203.2
Shakespeare       Romeo      27.0       19.0       46.0
Dante             Inferno    11.6       12.6       24.2
Run Code Online (Sandbox Code Playgroud)

我希望使用类似的东西meshgrid可以让我达到 75%。基本上,我想看看是否有可能用numpy(而不是熊猫)构建所有预计算值的结构来构建一个结构,以便我可以检索所有可能组合的上述结果。为简单起见,我们仅将SUM视为唯一可能的计算。也许这是一种圆整的询问方式,但可能numpy是这样做的支柱,还是我需要使用其他东西?

最后,如果不可能,numpy如何将其存储在 MDA 中?

meT*_*sky 9

我认为numpy 记录数组可用于此任务,以下是我基于记录数组的解决方案。

class rec_array():
    
    def __init__(self,author=None,book=None,year=None,sales=None):
        self.dtype = [('author','<U20'), ('book','<U20'),('year','<U20'),('sales',float)]
        self.rec_array = np.rec.fromarrays((author,book,year,sales),dtype=self.dtype)
        
    def add_record(self,author,book,year,sales):
        new_rec = np.rec.fromarrays((author,book,year,sales),dtype=self.dtype)
        if not self.rec_array.shape == ():
            self.rec_array = np.hstack((self.rec_array,new_rec))
        else:
            self.rec_array = new_rec
    
    def get_view(self,conditions):
        """
        conditions: 
            A list of conditions, for example 
            [["author",<,"Shakespeare"],["year","<=","2000"]]
        """
        mask = np.ones(self.rec_array.shape[0]).astype(bool)
        for item in conditions:
            field,op,target = item
            field_op = "self.rec_array['%s'] %s '%s'" % (field,op,target)
            mask &= eval(field_op)
        
        selected_sales = self.rec_array['sales'][mask]
        
        return np.sum(selected_sales)
Run Code Online (Sandbox Code Playgroud)

在此基础上rec_array,给定数据

author = 4*["Shakespeare"]+ 2*["Dante"]
book = 2*["Hamlet"] + 2*["Romeo"] + 2*["Inferno"]
year = 3*["2000", "2001"]
sales = [104.2, 99.0, 27.0, 19.0, 11.6, 12.6]
Run Code Online (Sandbox Code Playgroud)

我们创建一个实例

test = rec_array()
test.add_record(author,book,year,sales)
Run Code Online (Sandbox Code Playgroud)

例如,如果您想要莎士比亚的《罗密欧》的销量,您可以简单地执行此操作

test.get_view([["author","==","Shakespeare"],["book","==","Romeo"]])
Run Code Online (Sandbox Code Playgroud)

输出为 46.0

或者,你也可以这样做

test.get_view([["author","==","Shakespeare"],["year","<=","2000"]])
Run Code Online (Sandbox Code Playgroud)

输出为 131.2


Dan*_*ejo 5

对于数据结构,您可以定义以下类:

class Cube:

    def __init__(self, row_index, col_index, data):
        self.row_index = {r: i for i, r in enumerate(row_index)}
        self.col_index = {c: i for i, c in enumerate(col_index)}
        self.data = data

    def __getitem__(self, item):
        row, col = item
        return self.data[self.row_index[row] , self.col_index[col]]

    def __repr__(self):
        return repr(self.data)
Run Code Online (Sandbox Code Playgroud)

基本上是一个围绕二维 numpy 数组的轻型包装器。为了计算交叉表,您可以执行以下操作:

def _x_tab(rows, columns, values):
    """Function for computing the cross-tab of simple arrays"""
    unique_values_all_cols, idx = zip(*(np.unique(col, return_inverse=True) for col in [rows, columns]))

    shape_xt = [uniq_vals_col.size for uniq_vals_col in unique_values_all_cols]

    xt = np.zeros(shape_xt, dtype=np.float)
    np.add.at(xt, idx, values)

    return unique_values_all_cols, xt


def make_index(a, r):
    """Make array of tuples"""
    l = [tuple(row) for row in a[:, r]]
    return make_object_array(l)


def make_object_array(l):
    a = np.empty(len(l), dtype=object)
    a[:] = l
    return a


def fill_label(ar, le):
    """Fill missing parts with ALL label"""
    missing = tuple(["ALL"] * le)
    return [(e + missing)[:le] for e in ar]

def x_tab(rows, cols, values):
    """Main function for cross tabulation"""
    _, l_cols = rows.shape

    total_agg = []
    total_idx = []
    for i in range(l_cols + 1):
        (idx, _), agg = _x_tab(make_index(rows, list(range(i))), cols, values)
        total_idx.extend(fill_label(idx, l_cols))
        total_agg.append(agg)

    stacked_agg = np.vstack(total_agg)
    stacked_agg_total = stacked_agg.sum(axis=1).reshape(-1, 1)

    return Cube(total_idx, list(dict.fromkeys(cols)), np.concatenate((stacked_agg, stacked_agg_total), axis=1))
Run Code Online (Sandbox Code Playgroud)

假设输入一个arr数组:

[['Shakespeare' 'Hamlet' 2000 104.2]
 ['Shakespeare' 'Hamlet' 2001 99.0]
 ['Shakespeare' 'Romeo' 2000 27.0]
 ['Shakespeare' 'Romeo' 2001 19.0]
 ['Dante' 'Inferno' 2000 11.6]
 ['Dante' 'Inferno' 2001 12.6]]
Run Code Online (Sandbox Code Playgroud)

然后x_tab可以这样调用:

result = x_tab(arr[:, [0, 1]], arr[:, 2], arr[:, 3])
print(result)
Run Code Online (Sandbox Code Playgroud)

输出

array([[142.8, 130.6, 273.4],
       [ 11.6,  12.6,  24.2],
       [131.2, 118. , 249.2],
       [ 11.6,  12.6,  24.2],
       [104.2,  99. , 203.2],
       [ 27. ,  19. ,  46. ]])
Run Code Online (Sandbox Code Playgroud)

请注意,此表示 (repr) 仅用于显示结果,您可以根据需要进行更改。然后您可以按如下方式访问多维数据集的单元格:

print(result[('Dante', 'ALL'), 2001])
print(result[('Dante', 'Inferno'), 2001])
print(result[('Shakespeare', 'Hamlet'), 2000])
Run Code Online (Sandbox Code Playgroud)

输出

12.6
12.6
104.2
Run Code Online (Sandbox Code Playgroud)

请注意,大部分操作都在 _x_tab 函数中,该函数使用纯 numpy 函数。同时它为您选择的任何聚合函数提供了一个灵活的接口,只需在这一行更改ufunc

np.add.at(xt, idx, values)
Run Code Online (Sandbox Code Playgroud)

来自此列表中的任何其他人。有关更多信息,请参阅有关at运算符的文档。

可以在此处找到代码的工作副本。以上是基于这个要点

注意 这假设您为索引(rows 参数)传递多列。