21
21
22
22
import bigframes .constants as constants
23
23
import bigframes .core .blocks as blocks
24
+ import bigframes .core .convert
24
25
import bigframes .core .expression as ex
25
26
import bigframes .core .indexes as indexes
26
27
import bigframes .core .scalar as scalars
@@ -44,7 +45,15 @@ def __init__(
44
45
* ,
45
46
session : typing .Optional [bigframes .session .Session ] = None ,
46
47
):
47
- block = None
48
+ import bigframes .pandas
49
+
50
+ read_pandas_func = (
51
+ session .read_pandas
52
+ if (session is not None )
53
+ else (lambda x : bigframes .pandas .read_pandas (x ))
54
+ )
55
+
56
+ block : typing .Optional [blocks .Block ] = None
48
57
if copy is not None and not copy :
49
58
raise ValueError (
50
59
f"Series constructor only supports copy=True. { constants .FEEDBACK_LINK } "
@@ -55,29 +64,36 @@ def __init__(
55
64
assert index is None
56
65
block = data
57
66
58
- elif isinstance (data , SeriesMethods ):
59
- block = data ._block
67
+ elif isinstance (data , SeriesMethods ) or isinstance (data , pd .Series ):
68
+ if isinstance (data , pd .Series ):
69
+ data = read_pandas_func (data )
70
+ data_block = data ._block
60
71
if index is not None :
61
72
# reindex
62
- bf_index = indexes .Index (index )
73
+ bf_index = indexes .Index (index , session = session )
63
74
idx_block = bf_index ._block
64
75
idx_cols = idx_block .value_columns
65
- block_idx , _ = idx_block .join (block , how = "left" )
66
- block = block_idx .with_index_labels (bf_index .names )
76
+ block_idx , _ = idx_block .join (data_block , how = "left" )
77
+ data_block = block_idx .with_index_labels (bf_index .names )
78
+ block = data_block
67
79
68
- elif isinstance (data , indexes .Index ):
80
+ elif isinstance (data , indexes .Index ) or pd .api .types .is_list_like (data ):
81
+ data = indexes .Index (data , session = session )
69
82
if data .nlevels != 1 :
70
83
raise NotImplementedError ("Cannot interpret multi-index as Series." )
71
84
# Reset index to promote index columns to value columns, set default index
72
- block = data ._block .reset_index (drop = False )
85
+ data_block = data ._block .reset_index (drop = False )
73
86
if index is not None :
74
87
# Align by offset
75
- bf_index = indexes .Index (index )
76
- idx_block = bf_index ._block .reset_index (drop = False )
88
+ bf_index = indexes .Index (index , session = session )
89
+ idx_block = bf_index ._block .reset_index (
90
+ drop = False
91
+ ) # reset to align by offsets, and then reset back
77
92
idx_cols = idx_block .value_columns
78
- block , (l_mapping , _ ) = idx_block .join (block , how = "left" )
79
- block = block .set_index ([l_mapping [col ] for col in idx_cols ])
80
- block = block .with_index_labels (bf_index .names )
93
+ data_block , (l_mapping , _ ) = idx_block .join (data_block , how = "left" )
94
+ data_block = data_block .set_index ([l_mapping [col ] for col in idx_cols ])
95
+ data_block = data_block .with_index_labels (bf_index .names )
96
+ block = data_block
81
97
82
98
if block :
83
99
if name :
@@ -91,22 +107,17 @@ def __init__(
91
107
block .value_columns , ops .AsTypeOp (to_type = dtype )
92
108
)
93
109
else :
94
- import bigframes .pandas
95
-
96
110
pd_series = pd .Series (
97
111
data = data , index = index , dtype = dtype , name = name # type:ignore
98
112
)
99
113
pd_dataframe = pd_series .to_frame ()
100
114
if pd_series .name is None :
101
115
# to_frame will set default numeric column label if unnamed, but we do not support int column label, so must rename
102
116
pd_dataframe = pd_dataframe .set_axis (["unnamed_col" ], axis = 1 )
103
- if session :
104
- block = session .read_pandas (pd_dataframe )._get_block ()
105
- else :
106
- # Uses default global session
107
- block = bigframes .pandas .read_pandas (pd_dataframe )._get_block ()
117
+ block = read_pandas_func (pd_dataframe )._get_block () # type: ignore
108
118
if pd_series .name is None :
109
- block = block .with_column_labels ([None ])
119
+ block = block .with_column_labels ([None ]) # type: ignore
120
+ assert block is not None
110
121
self ._block : blocks .Block = block
111
122
112
123
@property
@@ -145,17 +156,16 @@ def _apply_binary_op(
145
156
reverse : bool = False ,
146
157
) -> series .Series :
147
158
"""Applies a binary operator to the series and other."""
148
- if isinstance (other , pd . Series ):
149
- # TODO: Convert to BigQuery DataFrames series
150
- raise NotImplementedError (
151
- f"Pandas series not supported as operand. { constants . FEEDBACK_LINK } "
159
+ if bigframes . core . convert . is_series_convertible (other ):
160
+ self_index = indexes . Index ( self . _block )
161
+ other_series = bigframes . core . convert . to_bf_series (
162
+ other , self_index , self . _block . session
152
163
)
153
- if isinstance (other , series .Series ):
154
- (self_col , other_col , block ) = self ._align (other , how = alignment )
164
+ (self_col , other_col , block ) = self ._align (other_series , how = alignment )
155
165
156
166
name = self ._name
157
167
if (
158
- isinstance (other , series . Series )
168
+ hasattr (other , "name" )
159
169
and other .name != self ._name
160
170
and alignment == "outer"
161
171
):
@@ -166,7 +176,7 @@ def _apply_binary_op(
166
176
block , result_id = block .project_expr (expr , name )
167
177
return series .Series (block .select_column (result_id ))
168
178
169
- else :
179
+ else : # Scalar binop
170
180
name = self ._name
171
181
expr = op .as_expr (
172
182
ex .const (other ) if reverse else self ._value_column ,
0 commit comments