maxframe 1.0.0rc1__cp39-cp39-win_amd64.whl → 1.0.0rc3__cp39-cp39-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of maxframe might be problematic. Click here for more details.

Files changed (138) hide show
  1. maxframe/_utils.cp39-win_amd64.pyd +0 -0
  2. maxframe/codegen.py +3 -6
  3. maxframe/config/config.py +49 -10
  4. maxframe/config/validators.py +42 -11
  5. maxframe/conftest.py +15 -2
  6. maxframe/core/__init__.py +2 -13
  7. maxframe/core/entity/__init__.py +0 -4
  8. maxframe/core/entity/objects.py +46 -3
  9. maxframe/core/entity/output_types.py +0 -3
  10. maxframe/core/entity/tests/test_objects.py +43 -0
  11. maxframe/core/entity/tileables.py +5 -78
  12. maxframe/core/graph/__init__.py +2 -2
  13. maxframe/core/graph/builder/__init__.py +0 -1
  14. maxframe/core/graph/builder/base.py +5 -4
  15. maxframe/core/graph/builder/tileable.py +4 -4
  16. maxframe/core/graph/builder/utils.py +4 -8
  17. maxframe/core/graph/core.cp39-win_amd64.pyd +0 -0
  18. maxframe/core/graph/entity.py +9 -33
  19. maxframe/core/operator/__init__.py +2 -9
  20. maxframe/core/operator/base.py +3 -5
  21. maxframe/core/operator/objects.py +0 -9
  22. maxframe/core/operator/utils.py +55 -0
  23. maxframe/dataframe/__init__.py +1 -1
  24. maxframe/dataframe/arithmetic/around.py +5 -17
  25. maxframe/dataframe/arithmetic/core.py +15 -7
  26. maxframe/dataframe/arithmetic/docstring.py +5 -55
  27. maxframe/dataframe/arithmetic/tests/test_arithmetic.py +22 -0
  28. maxframe/dataframe/core.py +5 -5
  29. maxframe/dataframe/datasource/date_range.py +2 -2
  30. maxframe/dataframe/datasource/read_odps_query.py +7 -1
  31. maxframe/dataframe/datasource/read_odps_table.py +3 -2
  32. maxframe/dataframe/datasource/tests/test_datasource.py +14 -0
  33. maxframe/dataframe/datastore/to_odps.py +1 -1
  34. maxframe/dataframe/groupby/cum.py +0 -1
  35. maxframe/dataframe/groupby/tests/test_groupby.py +4 -0
  36. maxframe/dataframe/indexing/add_prefix_suffix.py +1 -1
  37. maxframe/dataframe/indexing/rename.py +3 -37
  38. maxframe/dataframe/indexing/sample.py +0 -1
  39. maxframe/dataframe/indexing/set_index.py +68 -1
  40. maxframe/dataframe/merge/merge.py +236 -2
  41. maxframe/dataframe/merge/tests/test_merge.py +123 -0
  42. maxframe/dataframe/misc/apply.py +3 -10
  43. maxframe/dataframe/misc/case_when.py +1 -1
  44. maxframe/dataframe/misc/describe.py +2 -2
  45. maxframe/dataframe/misc/drop_duplicates.py +4 -25
  46. maxframe/dataframe/misc/eval.py +4 -0
  47. maxframe/dataframe/misc/pct_change.py +1 -83
  48. maxframe/dataframe/misc/transform.py +1 -30
  49. maxframe/dataframe/misc/value_counts.py +4 -17
  50. maxframe/dataframe/missing/dropna.py +1 -1
  51. maxframe/dataframe/missing/fillna.py +5 -5
  52. maxframe/dataframe/operators.py +1 -17
  53. maxframe/dataframe/reduction/core.py +2 -2
  54. maxframe/dataframe/sort/sort_values.py +1 -11
  55. maxframe/dataframe/statistics/quantile.py +5 -17
  56. maxframe/dataframe/utils.py +4 -7
  57. maxframe/io/objects/__init__.py +24 -0
  58. maxframe/io/objects/core.py +140 -0
  59. maxframe/io/objects/tensor.py +76 -0
  60. maxframe/io/objects/tests/__init__.py +13 -0
  61. maxframe/io/objects/tests/test_object_io.py +97 -0
  62. maxframe/{odpsio → io/odpsio}/__init__.py +3 -1
  63. maxframe/{odpsio → io/odpsio}/arrow.py +12 -8
  64. maxframe/{odpsio → io/odpsio}/schema.py +15 -12
  65. maxframe/io/odpsio/tableio.py +702 -0
  66. maxframe/io/odpsio/tests/__init__.py +13 -0
  67. maxframe/{odpsio → io/odpsio}/tests/test_schema.py +19 -18
  68. maxframe/{odpsio → io/odpsio}/tests/test_tableio.py +50 -23
  69. maxframe/{odpsio → io/odpsio}/tests/test_volumeio.py +4 -6
  70. maxframe/io/odpsio/volumeio.py +57 -0
  71. maxframe/learn/contrib/xgboost/classifier.py +26 -2
  72. maxframe/learn/contrib/xgboost/core.py +87 -2
  73. maxframe/learn/contrib/xgboost/dmatrix.py +3 -6
  74. maxframe/learn/contrib/xgboost/predict.py +21 -7
  75. maxframe/learn/contrib/xgboost/regressor.py +3 -10
  76. maxframe/learn/contrib/xgboost/train.py +27 -17
  77. maxframe/{core/operator/fuse.py → learn/core.py} +7 -10
  78. maxframe/lib/mmh3.cp39-win_amd64.pyd +0 -0
  79. maxframe/protocol.py +41 -17
  80. maxframe/remote/core.py +4 -8
  81. maxframe/serialization/__init__.py +1 -0
  82. maxframe/serialization/core.cp39-win_amd64.pyd +0 -0
  83. maxframe/serialization/serializables/core.py +48 -9
  84. maxframe/tensor/__init__.py +69 -2
  85. maxframe/tensor/arithmetic/isclose.py +1 -0
  86. maxframe/tensor/arithmetic/tests/test_arithmetic.py +21 -17
  87. maxframe/tensor/core.py +5 -136
  88. maxframe/tensor/datasource/array.py +3 -0
  89. maxframe/tensor/datasource/full.py +1 -1
  90. maxframe/tensor/datasource/tests/test_datasource.py +1 -1
  91. maxframe/tensor/indexing/flatnonzero.py +1 -1
  92. maxframe/tensor/merge/__init__.py +2 -0
  93. maxframe/tensor/merge/concatenate.py +98 -0
  94. maxframe/tensor/merge/tests/test_merge.py +30 -1
  95. maxframe/tensor/merge/vstack.py +70 -0
  96. maxframe/tensor/{base → misc}/__init__.py +2 -0
  97. maxframe/tensor/{base → misc}/atleast_1d.py +0 -2
  98. maxframe/tensor/misc/atleast_2d.py +70 -0
  99. maxframe/tensor/misc/atleast_3d.py +85 -0
  100. maxframe/tensor/misc/tests/__init__.py +13 -0
  101. maxframe/tensor/{base → misc}/transpose.py +22 -18
  102. maxframe/tensor/{base → misc}/unique.py +2 -2
  103. maxframe/tensor/operators.py +1 -7
  104. maxframe/tensor/random/core.py +1 -1
  105. maxframe/tensor/reduction/count_nonzero.py +1 -0
  106. maxframe/tensor/reduction/mean.py +1 -0
  107. maxframe/tensor/reduction/nanmean.py +1 -0
  108. maxframe/tensor/reduction/nanvar.py +2 -0
  109. maxframe/tensor/reduction/tests/test_reduction.py +12 -1
  110. maxframe/tensor/reduction/var.py +2 -0
  111. maxframe/tensor/statistics/quantile.py +2 -2
  112. maxframe/tensor/utils.py +2 -22
  113. maxframe/tests/utils.py +11 -2
  114. maxframe/typing_.py +4 -1
  115. maxframe/udf.py +8 -9
  116. maxframe/utils.py +32 -70
  117. {maxframe-1.0.0rc1.dist-info → maxframe-1.0.0rc3.dist-info}/METADATA +25 -25
  118. {maxframe-1.0.0rc1.dist-info → maxframe-1.0.0rc3.dist-info}/RECORD +133 -123
  119. {maxframe-1.0.0rc1.dist-info → maxframe-1.0.0rc3.dist-info}/WHEEL +1 -1
  120. maxframe_client/fetcher.py +60 -68
  121. maxframe_client/session/graph.py +8 -2
  122. maxframe_client/session/odps.py +58 -22
  123. maxframe_client/tests/test_fetcher.py +21 -3
  124. maxframe_client/tests/test_session.py +27 -4
  125. maxframe/core/entity/chunks.py +0 -68
  126. maxframe/core/entity/fuse.py +0 -73
  127. maxframe/core/graph/builder/chunk.py +0 -430
  128. maxframe/odpsio/tableio.py +0 -322
  129. maxframe/odpsio/volumeio.py +0 -95
  130. /maxframe/{odpsio → core/entity}/tests/__init__.py +0 -0
  131. /maxframe/{tensor/base/tests → io}/__init__.py +0 -0
  132. /maxframe/{odpsio → io/odpsio}/tests/test_arrow.py +0 -0
  133. /maxframe/tensor/{base → misc}/astype.py +0 -0
  134. /maxframe/tensor/{base → misc}/broadcast_to.py +0 -0
  135. /maxframe/tensor/{base → misc}/ravel.py +0 -0
  136. /maxframe/tensor/{base/tests/test_base.py → misc/tests/test_misc.py} +0 -0
  137. /maxframe/tensor/{base → misc}/where.py +0 -0
  138. {maxframe-1.0.0rc1.dist-info → maxframe-1.0.0rc3.dist-info}/top_level.txt +0 -0
@@ -12,10 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Generator, Union
15
+ from typing import Generator
16
16
 
17
17
  from ...mode import enter_mode
18
- from ..entity import ChunkGraph, TileableGraph
18
+ from ..entity import TileableGraph
19
19
  from .base import AbstractGraphBuilder
20
20
 
21
21
 
@@ -26,9 +26,9 @@ class TileableGraphBuilder(AbstractGraphBuilder):
26
26
  super().__init__(graph=graph)
27
27
 
28
28
  @enter_mode(build=True, kernel=True)
29
- def _build(self) -> Union[TileableGraph, ChunkGraph]:
29
+ def _build(self) -> TileableGraph:
30
30
  self._add_nodes(self._graph, list(self._graph.result_tileables), set())
31
31
  return self._graph
32
32
 
33
- def build(self) -> Generator[Union[TileableGraph, ChunkGraph], None, None]:
33
+ def build(self) -> Generator[TileableGraph, None, None]:
34
34
  yield self._build()
@@ -13,12 +13,11 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import itertools
16
- from typing import List, Union
16
+ from typing import List
17
17
 
18
18
  from ....typing_ import TileableType
19
19
  from ...mode import enter_mode
20
- from ..entity import ChunkGraph, TileableGraph
21
- from .chunk import ChunkGraphBuilder
20
+ from ..entity import EntityGraph, TileableGraph
22
21
  from .tileable import TileableGraphBuilder
23
22
 
24
23
 
@@ -28,14 +27,11 @@ def build_graph(
28
27
  tile: bool = False,
29
28
  fuse_enabled: bool = True,
30
29
  **chunk_graph_build_kwargs
31
- ) -> Union[TileableGraph, ChunkGraph]:
30
+ ) -> EntityGraph:
32
31
  tileables = list(itertools.chain(*(tileable.op.outputs for tileable in tileables)))
33
32
  tileable_graph = TileableGraph(tileables)
34
33
  tileable_graph_builder = TileableGraphBuilder(tileable_graph)
35
34
  tileable_graph = next(tileable_graph_builder.build())
36
35
  if not tile:
37
36
  return tileable_graph
38
- chunk_graph_builder = ChunkGraphBuilder(
39
- tileable_graph, fuse_enabled=fuse_enabled, **chunk_graph_build_kwargs
40
- )
41
- return next(chunk_graph_builder.build())
37
+ raise NotImplementedError
Binary file
@@ -13,9 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from abc import ABCMeta, abstractmethod
16
- from typing import Dict, Iterable, List, Union
16
+ from typing import Dict, Iterable, List
17
17
 
18
- from ...core import Chunk, Tileable
18
+ from ...core import Tileable
19
19
  from ...serialization.core import buffered
20
20
  from ...serialization.serializables import BoolField, DictField, ListField, Serializable
21
21
  from ...serialization.serializables.core import SerializableSerializer
@@ -97,26 +97,6 @@ class TileableGraph(EntityGraph, Iterable[Tileable]):
97
97
  return self._logic_key
98
98
 
99
99
 
100
- class ChunkGraph(EntityGraph, Iterable[Chunk]):
101
- _result_chunks: List[Chunk]
102
-
103
- def __init__(self, result_chunks: List[Chunk] = None):
104
- super().__init__()
105
- self._result_chunks = result_chunks
106
-
107
- @property
108
- def result_chunks(self):
109
- return self._result_chunks
110
-
111
- @property
112
- def results(self):
113
- return self._result_chunks
114
-
115
- @results.setter
116
- def results(self, new_results):
117
- self._result_chunks = new_results
118
-
119
-
120
100
  class SerializableGraph(Serializable):
121
101
  _is_chunk = BoolField("is_chunk")
122
102
  # TODO(qinxuye): remove this logic when we handle fetch elegantly,
@@ -132,12 +112,11 @@ class SerializableGraph(Serializable):
132
112
  _results = ListField("results")
133
113
 
134
114
  @classmethod
135
- def from_graph(cls, graph: Union[TileableGraph, ChunkGraph]) -> "SerializableGraph":
115
+ def from_graph(cls, graph: EntityGraph) -> "SerializableGraph":
136
116
  from ..operator import Fetch
137
117
 
138
- is_chunk = isinstance(graph, ChunkGraph)
139
118
  return SerializableGraph(
140
- _is_chunk=is_chunk,
119
+ _is_chunk=False,
141
120
  _fetch_nodes=[chunk for chunk in graph if isinstance(chunk.op, Fetch)],
142
121
  _nodes=graph._nodes,
143
122
  _predecessors=graph._predecessors,
@@ -145,9 +124,8 @@ class SerializableGraph(Serializable):
145
124
  _results=graph.results,
146
125
  )
147
126
 
148
- def to_graph(self) -> Union[TileableGraph, ChunkGraph]:
149
- graph_cls = ChunkGraph if self._is_chunk else TileableGraph
150
- graph = graph_cls(self._results)
127
+ def to_graph(self) -> EntityGraph:
128
+ graph = TileableGraph(self._results)
151
129
  graph._nodes.update(self._nodes)
152
130
  graph._predecessors.update(self._predecessors)
153
131
  graph._successors.update(self._successors)
@@ -156,14 +134,12 @@ class SerializableGraph(Serializable):
156
134
 
157
135
  class GraphSerializer(SerializableSerializer):
158
136
  @buffered
159
- def serial(self, obj: Union[TileableGraph, ChunkGraph], context: Dict):
137
+ def serial(self, obj: EntityGraph, context: Dict):
160
138
  serializable_graph = SerializableGraph.from_graph(obj)
161
139
  return [], [serializable_graph], False
162
140
 
163
- def deserial(
164
- self, serialized: List, context: Dict, subs: List
165
- ) -> Union[TileableGraph, ChunkGraph]:
166
- serializable_graph: SerializableGraph = subs[0]
141
+ def deserial(self, serialized: List, context: Dict, subs: List) -> TileableGraph:
142
+ serializable_graph: EntityGraph = subs[0]
167
143
  return serializable_graph.to_graph()
168
144
 
169
145
 
@@ -22,13 +22,6 @@ from .base import (
22
22
  )
23
23
  from .core import TileableOperatorMixin, estimate_size, execute
24
24
  from .fetch import Fetch, FetchMixin, FetchShuffle, ShuffleFetchType
25
- from .fuse import Fuse, FuseChunkMixin
26
- from .objects import (
27
- MergeDictOperator,
28
- ObjectFetch,
29
- ObjectFuseChunk,
30
- ObjectFuseChunkMixin,
31
- ObjectOperator,
32
- ObjectOperatorMixin,
33
- )
25
+ from .objects import MergeDictOperator, ObjectFetch, ObjectOperator, ObjectOperatorMixin
34
26
  from .shuffle import MapReduceOperator, ShuffleProxy
27
+ from .utils import add_fetch_builder, build_fetch
@@ -12,11 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import functools
16
15
  import weakref
17
16
  from copy import deepcopy
18
17
  from enum import Enum
19
- from functools import partial
18
+ from functools import lru_cache, partial
20
19
  from typing import Any, Dict, List, Optional, Tuple, Type, Union
21
20
 
22
21
  from ...serialization.core import Placeholder
@@ -37,7 +36,6 @@ from ...serialization.serializables.core import SerializableSerializer
37
36
  from ...typing_ import OperatorType
38
37
  from ...utils import AttributeDict, classproperty, get_user_call_point, tokenize
39
38
  from ..base import Base
40
- from ..entity.chunks import Chunk
41
39
  from ..entity.core import ENTITY_TYPE, Entity, EntityData
42
40
  from ..entity.output_types import OutputType
43
41
  from ..entity.tileables import Tileable
@@ -90,7 +88,7 @@ class SchedulingHint(Serializable):
90
88
  priority = Int32Field("priority", default=None)
91
89
 
92
90
  @classproperty
93
- @functools.lru_cache(1)
91
+ @lru_cache(1)
94
92
  def all_hint_names(cls):
95
93
  return list(cls._FIELDS)
96
94
 
@@ -341,7 +339,7 @@ class Operator(Base, OperatorLogicKeyGeneratorMixin, metaclass=OperatorMetaclass
341
339
  raise ValueError("Outputs' size exceeds limitation")
342
340
 
343
341
  @property
344
- def outputs(self) -> List[Union[Chunk, Tileable]]:
342
+ def outputs(self) -> List[Tileable]:
345
343
  outputs = self._outputs
346
344
  if outputs:
347
345
  return [ref() for ref in outputs]
@@ -17,7 +17,6 @@ from ..entity import OutputType, register_fetch_class
17
17
  from .base import Operator
18
18
  from .core import TileableOperatorMixin
19
19
  from .fetch import Fetch, FetchMixin
20
- from .fuse import Fuse, FuseChunkMixin
21
20
 
22
21
 
23
22
  class ObjectOperator(Operator):
@@ -28,14 +27,6 @@ class ObjectOperatorMixin(TileableOperatorMixin):
28
27
  _output_type_ = OutputType.object
29
28
 
30
29
 
31
- class ObjectFuseChunkMixin(FuseChunkMixin, ObjectOperatorMixin):
32
- __slots__ = ()
33
-
34
-
35
- class ObjectFuseChunk(ObjectFuseChunkMixin, Fuse):
36
- pass
37
-
38
-
39
30
  class ObjectFetch(FetchMixin, ObjectOperatorMixin, Fetch):
40
31
  _output_type_ = OutputType.object
41
32
 
@@ -0,0 +1,55 @@
1
+ # Copyright 1999-2024 Alibaba Group Holding Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from ...typing_ import EntityType, TileableType
16
+ from ..entity import TILEABLE_TYPE
17
+
18
+
19
+ def build_fetch_tileable(tileable: TileableType) -> TileableType:
20
+ if tileable.is_coarse():
21
+ chunks = None
22
+ else:
23
+ chunks = []
24
+ for c in tileable.chunks:
25
+ fetch_chunk = build_fetch(c, index=c.index)
26
+ chunks.append(fetch_chunk)
27
+
28
+ tileable_op = tileable.op
29
+ params = tileable.params.copy()
30
+
31
+ new_op = tileable_op.get_fetch_op_cls(tileable)(_id=tileable_op.id)
32
+ return new_op.new_tileables(
33
+ None,
34
+ chunks=chunks,
35
+ nsplits=tileable.nsplits,
36
+ _key=tileable.key,
37
+ _id=tileable.id,
38
+ **params,
39
+ )[0]
40
+
41
+
42
+ _type_to_builder = [
43
+ (TILEABLE_TYPE, build_fetch_tileable),
44
+ ]
45
+
46
+
47
+ def build_fetch(entity: EntityType, **kw) -> EntityType:
48
+ for entity_types, func in _type_to_builder:
49
+ if isinstance(entity, entity_types):
50
+ return func(entity, **kw)
51
+ raise TypeError(f"Type {type(entity)} not supported")
52
+
53
+
54
+ def add_fetch_builder(entity_type, builder_func):
55
+ _type_to_builder.append((entity_type, builder_func))
@@ -54,7 +54,7 @@ from .reduction import CustomReduction, unique
54
54
  from .tseries.to_datetime import to_datetime
55
55
 
56
56
  try:
57
- from pandas import NA, Timestamp
57
+ from pandas import NA, NaT, Timestamp
58
58
  except ImportError: # pragma: no cover
59
59
  pass
60
60
 
@@ -43,20 +43,20 @@ def around(df, decimals=0, *args, **kwargs):
43
43
  return op(df)
44
44
 
45
45
 
46
+ # FIXME Series input of decimals not supported yet
46
47
  around.__frame_doc__ = """
47
48
  Round a DataFrame to a variable number of decimal places.
48
49
 
49
50
  Parameters
50
51
  ----------
51
- decimals : int, dict, Series
52
+ decimals : int, dict
52
53
  Number of decimal places to round each column to. If an int is
53
54
  given, round each column to the same number of places.
54
55
  Otherwise dict and Series round to variable numbers of places.
55
56
  Column names should be in the keys if `decimals` is a
56
- dict-like, or in the index if `decimals` is a Series. Any
57
- columns not included in `decimals` will be left as is. Elements
58
- of `decimals` which are not columns of the input will be
59
- ignored.
57
+ dict-like. Any columns not included in `decimals` will be left
58
+ as is. Elements of `decimals` which are not columns of the
59
+ input will be ignored.
60
60
  *args
61
61
  Additional keywords have no effect but might be accepted for
62
62
  compatibility with numpy.
@@ -107,18 +107,6 @@ places as value
107
107
  1 0.0 1.0
108
108
  2 0.7 0.0
109
109
  3 0.2 0.0
110
-
111
- Using a Series, the number of places for specific columns can be
112
- specified with the column names as index and the number of
113
- decimal places as value
114
-
115
- >>> decimals = md.Series([0, 1], index=['cats', 'dogs'])
116
- >>> df.round(decimals).execute()
117
- dogs cats
118
- 0 0.2 0.0
119
- 1 0.0 1.0
120
- 2 0.7 0.0
121
- 3 0.2 0.0
122
110
  """
123
111
  around.__series_doc__ = """
124
112
  Round each value in a Series to the given number of decimals.
@@ -39,7 +39,7 @@ class DataFrameBinOpMixin(DataFrameOperatorMixin):
39
39
  raise NotImplementedError
40
40
 
41
41
  @classmethod
42
- def _calc_properties(cls, x1, x2=None, axis="columns"):
42
+ def _calc_properties(cls, x1, x2=None, axis="columns", level=None):
43
43
  if isinstance(x1, DATAFRAME_TYPE) and (
44
44
  x2 is None or pd.api.types.is_scalar(x2) or isinstance(x2, TENSOR_TYPE)
45
45
  ):
@@ -108,7 +108,9 @@ class DataFrameBinOpMixin(DataFrameOperatorMixin):
108
108
  index = copy.copy(x1.index_value)
109
109
  index_shape = x1.shape[0]
110
110
  else:
111
- index = infer_index_value(x1.index_value, x2.index_value)
111
+ index = infer_index_value(
112
+ x1.index_value, x2.index_value, level=level
113
+ )
112
114
  if index.key == x1.index_value.key == x2.index_value.key and (
113
115
  not np.isnan(x1.shape[0]) or not np.isnan(x2.shape[0])
114
116
  ):
@@ -141,7 +143,9 @@ class DataFrameBinOpMixin(DataFrameOperatorMixin):
141
143
  column_shape = len(dtypes)
142
144
  else: # pragma: no cover
143
145
  dtypes = x1.dtypes # FIXME
144
- columns = infer_index_value(x1.columns_value, x2.index_value)
146
+ columns = infer_index_value(
147
+ x1.columns_value, x2.index_value, level=level
148
+ )
145
149
  column_shape = np.nan
146
150
  else:
147
151
  assert axis == "index" or axis == 0
@@ -169,7 +173,9 @@ class DataFrameBinOpMixin(DataFrameOperatorMixin):
169
173
  ],
170
174
  index=x1.dtypes.index,
171
175
  )
172
- index = infer_index_value(x1.index_value, x2.index_value)
176
+ index = infer_index_value(
177
+ x1.index_value, x2.index_value, level=level
178
+ )
173
179
  index_shape = np.nan
174
180
  return {
175
181
  "shape": (index_shape, column_shape),
@@ -187,7 +193,9 @@ class DataFrameBinOpMixin(DataFrameOperatorMixin):
187
193
  index = copy.copy(x1.index_value)
188
194
  index_shape = x1.shape[0]
189
195
  else:
190
- index = infer_index_value(x1.index_value, x2.index_value)
196
+ index = infer_index_value(
197
+ x1.index_value, x2.index_value, level=level
198
+ )
191
199
  if index.key == x1.index_value.key == x2.index_value.key and (
192
200
  not np.isnan(x1.shape[0]) or not np.isnan(x2.shape[0])
193
201
  ):
@@ -237,14 +245,14 @@ class DataFrameBinOpMixin(DataFrameOperatorMixin):
237
245
  self._check_inputs(x1, x2)
238
246
  if isinstance(x1, DATAFRAME_TYPE) or isinstance(x2, DATAFRAME_TYPE):
239
247
  df1, df2 = (x1, x2) if isinstance(x1, DATAFRAME_TYPE) else (x2, x1)
240
- kw = self._calc_properties(df1, df2, axis=self.axis)
248
+ kw = self._calc_properties(df1, df2, axis=self.axis, level=self.level)
241
249
  if not pd.api.types.is_scalar(df2):
242
250
  return self.new_dataframe([x1, x2], **kw)
243
251
  else:
244
252
  return self.new_dataframe([df1], **kw)
245
253
  if isinstance(x1, SERIES_TYPE) or isinstance(x2, SERIES_TYPE):
246
254
  s1, s2 = (x1, x2) if isinstance(x1, SERIES_TYPE) else (x2, x1)
247
- kw = self._calc_properties(s1, s2)
255
+ kw = self._calc_properties(s1, s2, level=self.level)
248
256
  if not pd.api.types.is_scalar(s2):
249
257
  return self.new_series([x1, x2], **kw)
250
258
  else:
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ # FIXME:https://github.com/aliyun/alibabacloud-odps-maxframe-client/issues/17
15
16
  _flex_doc_FRAME = """
16
17
  Get {desc} of dataframe and other, element-wise (binary operator `{op_name}`).
17
18
  Equivalent to ``{equiv}``, but with support to substitute a fill_value
@@ -127,44 +128,15 @@ circle 0
127
128
  triangle 3
128
129
  rectangle 4
129
130
 
130
- >>> (df * other).execute()
131
- angles degrees
132
- circle 0 NaN
133
- triangle 9 NaN
134
- rectangle 16 NaN
135
-
136
131
  >>> df.mul(other, fill_value=0).execute()
137
132
  angles degrees
138
133
  circle 0 0.0
139
134
  triangle 9 0.0
140
135
  rectangle 16 0.0
141
136
 
142
- Divide by a MultiIndex by level.
143
-
144
- >>> df_multindex = md.DataFrame({{'angles': [0, 3, 4, 4, 5, 6],
145
- ... 'degrees': [360, 180, 360, 360, 540, 720]}},
146
- ... index=[['A', 'A', 'A', 'B', 'B', 'B'],
147
- ... ['circle', 'triangle', 'rectangle',
148
- ... 'square', 'pentagon', 'hexagon']])
149
- >>> df_multindex.execute()
150
- angles degrees
151
- A circle 0 360
152
- triangle 3 180
153
- rectangle 4 360
154
- B square 4 360
155
- pentagon 5 540
156
- hexagon 6 720
157
-
158
- >>> df.div(df_multindex, level=1, fill_value=0).execute()
159
- angles degrees
160
- A circle NaN 1.0
161
- triangle 1.0 1.0
162
- rectangle 1.0 1.0
163
- B square 0.0 0.0
164
- pentagon 0.0 0.0
165
- hexagon 0.0 0.0
166
137
  """
167
138
 
139
+ # FIXME:https://github.com/aliyun/alibabacloud-odps-maxframe-client/issues/28
168
140
  _flex_doc_SERIES = """
169
141
  Return {desc} of series and other, element-wise (binary operator `{op_name}`).
170
142
 
@@ -213,6 +185,7 @@ e NaN
213
185
  dtype: float64
214
186
  """
215
187
 
188
+ # FIXME: https://github.com/aliyun/alibabacloud-odps-maxframe-client/issues/48
216
189
  _flex_comp_doc_FRAME = """
217
190
  Get {desc} of dataframe and other, element-wise (binary operator `{op_name}`).
218
191
  Among flexible wrappers (`eq`, `ne`, `le`, `lt`, `ge`, `gt`) to comparison
@@ -257,7 +230,8 @@ Mismatched indices will be unioned together.
257
230
 
258
231
  Examples
259
232
  --------
260
- >>> df = pd.DataFrame({{'cost': [250, 150, 100],
233
+ >>> import maxframe.dataframe as md
234
+ >>> df = md.DataFrame({{'cost': [250, 150, 100],
261
235
  ... 'revenue': [100, 250, 300]}},
262
236
  ... index=['A', 'B', 'C'])
263
237
  >>> df.execute()
@@ -332,30 +306,6 @@ A False False
332
306
  B False False
333
307
  C False True
334
308
  D False False
335
-
336
- Compare to a MultiIndex by level.
337
-
338
- >>> df_multindex = pd.DataFrame({{'cost': [250, 150, 100, 150, 300, 220],
339
- ... 'revenue': [100, 250, 300, 200, 175, 225]}},
340
- ... index=[['Q1', 'Q1', 'Q1', 'Q2', 'Q2', 'Q2'],
341
- ... ['A', 'B', 'C', 'A', 'B', 'C']])
342
- >>> df_multindex.execute()
343
- cost revenue
344
- Q1 A 250 100
345
- B 150 250
346
- C 100 300
347
- Q2 A 150 200
348
- B 300 175
349
- C 220 225
350
-
351
- >>> df.le(df_multindex, level=1).execute()
352
- cost revenue
353
- Q1 A True True
354
- B True True
355
- C True True
356
- Q2 A False True
357
- B True False
358
- C True False
359
309
  """
360
310
 
361
311
 
@@ -239,6 +239,28 @@ def test_dataframe_and_series_with_shuffle(func_name, func_opts):
239
239
  assert df2.columns_value.key != df1.columns_value.key
240
240
 
241
241
 
242
+ @pytest.mark.parametrize("func_name, func_opts", binary_functions.items())
243
+ def test_dataframe_and_series_with_multiindex(func_name, func_opts):
244
+ data1 = pd.DataFrame(
245
+ np.random.rand(10, 10),
246
+ index=pd.MultiIndex.from_arrays(
247
+ [list("AAAAABBBBB"), [4, 9, 3, 2, 1, 5, 8, 6, 7, 10]]
248
+ ),
249
+ columns=[4, 1, 3, 2, 10, 5, 9, 8, 6, 7],
250
+ )
251
+ data1 = to_boolean_if_needed(func_opts.func_name, data1)
252
+ df1 = from_pandas(data1, chunk_size=5)
253
+ s1 = from_pandas_series(data1[10].reset_index(level=0, drop=True), chunk_size=6)
254
+
255
+ df2 = getattr(df1, func_opts.func_name)(s1, level=1, axis=0)
256
+
257
+ # test df2's index and columns
258
+ assert df2.shape == (np.nan, df1.shape[1])
259
+ assert df2.index_value.key != df1.index_value.key
260
+ assert df2.index_value.names == df1.index_value.names
261
+ assert df2.columns_value.key == df1.columns_value.key
262
+
263
+
242
264
  @pytest.mark.parametrize("func_name, func_opts", binary_functions.items())
243
265
  def test_series_and_series_with_align_map(func_name, func_opts):
244
266
  data1 = pd.DataFrame(
@@ -1086,11 +1086,11 @@ class Series(HasShapeTileable, _ToPandasMixin):
1086
1086
  --------
1087
1087
  >>> import maxframe.dataframe as md
1088
1088
  >>> s = md.Series({'a': 1, 'b': 2, 'c': 3})
1089
- >>> s.ndim.execute()
1089
+ >>> s.ndim
1090
1090
  1
1091
1091
 
1092
1092
  >>> df = md.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
1093
- >>> df.ndim.execute()
1093
+ >>> df.ndim
1094
1094
  2
1095
1095
  """
1096
1096
  return super().ndim
@@ -1520,7 +1520,7 @@ class BaseDataFrameData(HasShapeTileableData, _ToPandasMixin):
1520
1520
  self._columns_value = parse_index(dtypes.index, store_data=True)
1521
1521
  self._dtypes_value = DtypesValue(key=tokenize(dtypes), value=dtypes)
1522
1522
  new_shape = list(self._shape)
1523
- new_shape[0] = len(dtypes)
1523
+ new_shape[-1] = len(dtypes)
1524
1524
  self._shape = tuple(new_shape)
1525
1525
 
1526
1526
  @property
@@ -1761,11 +1761,11 @@ class DataFrame(HasShapeTileable, _ToPandasMixin):
1761
1761
  --------
1762
1762
  >>> import maxframe.dataframe as md
1763
1763
  >>> s = md.Series({'a': 1, 'b': 2, 'c': 3})
1764
- >>> s.ndim.execute()
1764
+ >>> s.ndim
1765
1765
  1
1766
1766
 
1767
1767
  >>> df = md.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
1768
- >>> df.ndim.execute()
1768
+ >>> df.ndim
1769
1769
  2
1770
1770
  """
1771
1771
  return super().ndim
@@ -22,7 +22,7 @@ from pandas._libs.tslibs import timezones
22
22
  from pandas.tseries.frequencies import to_offset
23
23
  from pandas.tseries.offsets import Tick
24
24
 
25
- from ... import opcodes as OperandDef
25
+ from ... import opcodes
26
26
  from ...core import OutputType
27
27
  from ...serialization.serializables import AnyField, BoolField, Int64Field, StringField
28
28
  from ...utils import no_default, pd_release_version
@@ -117,7 +117,7 @@ def generate_range_count(
117
117
 
118
118
 
119
119
  class DataFrameDateRange(DataFrameOperator, DataFrameOperatorMixin):
120
- _op_type_ = OperandDef.DATE_RANGE
120
+ _op_type_ = opcodes.DATE_RANGE
121
121
 
122
122
  start = AnyField("start")
123
123
  end = AnyField("end")
@@ -24,7 +24,7 @@ from odps.types import Column, OdpsSchema, validate_data_type
24
24
  from ... import opcodes
25
25
  from ...core import OutputType
26
26
  from ...core.graph import DAG
27
- from ...odpsio import odps_schema_to_pandas_dtypes
27
+ from ...io.odpsio import odps_schema_to_pandas_dtypes
28
28
  from ...serialization.serializables import (
29
29
  AnyField,
30
30
  BoolField,
@@ -47,6 +47,7 @@ _EXPLAIN_TASK_SCHEMA_REGEX = re.compile(
47
47
  re.MULTILINE,
48
48
  )
49
49
  _EXPLAIN_COLUMN_REGEX = re.compile(r"([^\(]+) \(([^)]+)\)(?:| AS ([^ ]+))(?:\n|$)")
50
+ _ANONYMOUS_COL_REGEX = re.compile(r"^_c\d+$")
50
51
 
51
52
 
52
53
  @dataclasses.dataclass
@@ -272,6 +273,11 @@ def read_odps_query(
272
273
  explain_str = list(inst.get_task_results().values())[0]
273
274
 
274
275
  odps_schema = _parse_explained_schema(explain_str)
276
+
277
+ for col in odps_schema.columns:
278
+ if _ANONYMOUS_COL_REGEX.match(col.name) and col.name not in query:
279
+ raise ValueError("Need to specify names for all columns in SELECT clause.")
280
+
275
281
  dtypes = odps_schema_to_pandas_dtypes(odps_schema)
276
282
 
277
283
  if not index_col:
@@ -23,7 +23,7 @@ from odps.utils import to_timestamp
23
23
 
24
24
  from ... import opcodes
25
25
  from ...core import OutputType
26
- from ...odpsio import odps_schema_to_pandas_dtypes
26
+ from ...io.odpsio import odps_schema_to_pandas_dtypes
27
27
  from ...serialization.serializables import (
28
28
  AnyField,
29
29
  BoolField,
@@ -119,9 +119,10 @@ class DataFrameReadODPSTable(
119
119
  return self.new_tileable(
120
120
  [],
121
121
  None,
122
- shape=shape,
122
+ shape=shape[:1],
123
123
  name=getattr(index_value, "name", None),
124
124
  names=getattr(index_value, "names", None),
125
+ dtype=self.index_dtypes.iloc[0],
125
126
  index_value=index_value,
126
127
  chunk_bytes=chunk_bytes,
127
128
  chunk_size=chunk_size,
@@ -21,6 +21,7 @@ import pytest
21
21
  from odps import ODPS
22
22
 
23
23
  from .... import tensor as mt
24
+ from ....core import OutputType
24
25
  from ....tests.utils import tn
25
26
  from ....utils import lazy_import
26
27
  from ... import read_odps_query, read_odps_table
@@ -295,6 +296,15 @@ def test_from_odps_table():
295
296
  ),
296
297
  )
297
298
 
299
+ out_idx = read_odps_table(
300
+ test_table,
301
+ columns=[],
302
+ index_col=["col1", "col2"],
303
+ output_type=OutputType.index,
304
+ )
305
+ assert out_idx.names == ["col1", "col2"]
306
+ assert out_idx.shape == (np.nan,)
307
+
298
308
  test_table.drop()
299
309
  test_parted_table.drop()
300
310
 
@@ -319,6 +329,10 @@ def test_from_odps_query():
319
329
  read_odps_query(f"CREATE TABLE dummy_table AS SELECT * FROM {table1_name}")
320
330
  assert "instant query" in err_info.value.args[0]
321
331
 
332
+ with pytest.raises(ValueError) as err_info:
333
+ read_odps_query(f"SELECT col1, col2 + col3 FROM {table1_name}")
334
+ assert "names" in err_info.value.args[0]
335
+
322
336
  query1 = f"SELECT * FROM {table1_name} WHERE col1 > 10"
323
337
  df = read_odps_query(query1)
324
338
  assert df.op.query == query1