microsoft/qlib · error · ValueError
stock data from resam_ts_data must be a number, pd.Series or
Error message
stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame
What it means
PandasQuote.get_data feeds self.data[stock_id][field] through resam_ts_data and then accepts only None, scalars (bool/int/float/np.number), or pd.Series results. Anything else — in practice a pd.DataFrame — falls into the final else and raises this ValueError. Note the message mentions pd.DataFrame even though no DataFrame branch exists: DataFrames are explicitly unsupported here.
Source
Thrown at qlib/backtest/high_performance_ds.py:125
for stock_id, stock_val in quote_df.groupby(level="instrument", group_keys=False):
quote_dict[stock_id] = stock_val.droplevel(level="instrument")
self.data = quote_dict
def get_all_stock(self):
return self.data.keys()
def get_data(self, stock_id, start_time, end_time, field, method=None):
if method == "ts_data_last":
method = ts_data_last
stock_data = resam_ts_data(self.data[stock_id][field], start_time, end_time, method=method)
if stock_data is None:
return None
elif isinstance(stock_data, (bool, np.bool_, int, float, np.number)):
return stock_data
elif isinstance(stock_data, pd.Series):
return idd.SingleData(stock_data)
else:
raise ValueError(f"stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame")
class NumpyQuote(BaseQuote):
def __init__(self, quote_df: pd.DataFrame, freq: str, region: str = "cn") -> None:
"""NumpyQuote
Parameters
----------
quote_df : pd.DataFrame
the init dataframe from qlib.
self.data : Dict(stock_id, IndexData.DataFrame)
"""
super().__init__(quote_df=quote_df, freq=freq)
quote_dict = {}
for stock_id, stock_val in quote_df.groupby(level="instrument", group_keys=False):
quote_dict[stock_id] = idd.MultiData(stock_val.droplevel(level="instrument"))
quote_dict[stock_id].sort_index() # To support more flexible slicing, we must sort data first
self.data = quote_dictView on GitHub (pinned to 79633dd950)
Solutions
- Pass a single column name as field (a str like "$close") so self.data[stock_id][field] is a pd.Series
- Check quote_df for duplicated column names: quote_df.columns[quote_df.columns.duplicated()] and drop duplicates before constructing PandasQuote
- Use only documented methods: None, "last", "all", "sum", "mean", "ts_data_last"
- If you truly need multi-field fetches, fetch each field with a separate get_data call or use NumpyQuote which returns the underlying IndexData slice
Example fix
# before
quote.get_data("SH600000", "2010-01-04", "2010-01-06", field=["$close", "$volume"])
# after
for f in ("$close", "$volume"):
quote.get_data("SH600000", "2010-01-04", "2010-01-06", field=f) Defensive patterns
Strategy: validation
Validate before calling
# before calling get_data, confirm the field selects exactly one Series column
cols = quote_df.columns
assert isinstance(field, str) and (cols == field).sum() == 1, f"field {field!r} must match exactly one column" Type guard
def is_single_field(quote_df, field) -> bool:
import pandas as pd
return isinstance(field, str) and isinstance(quote_df.iloc[:1][0:1].droplevel(level='datetime'), pd.DataFrame)[field] if False else (isinstance(field, str) and (quote_df.columns == field).sum() == 1) Try / catch
try:
val = quote.get_data(sid, t0, t1, field)
except ValueError as e:
if "must be a number, pd.Series or pd.DataFrame" in str(e):
raise TypeError(f"field {field!r} selected multiple columns; pass one field at a time") from e
raise Prevention
- Always pass field as a single str like '$close', never a list
- Drop duplicated columns after building quote_df: quote_df = quote_df.loc[:, ~quote_df.columns.duplicated()]
- Restrict method to the documented set {None, 'last', 'all', 'sum', 'mean', 'ts_data_last'}
When it happens
Trigger: Passing a field selector that returns multiple columns per instrument, e.g. field=["$close","$volume"] or a field name that maps to duplicated columns in quote_df; calling get_data with an unsupported method string that makes resam_ts_data return a DataFrame instead of a Series/scalar.
Common situations: quote_df built from a multi-field dump where field indexing yields a DataFrame; refactoring get_data calls from SingleData-style APIs that accepted lists of fields; copy-pasting a method name not supported by resam_ts_data.
Related errors
- Please implement the `get_data` method
- {freq} is not supported in NumpyQuote
- {method} is not supported
- trade_calendar is necessary for getting TradeRangeByTime.
- The decision didn't provide an index range
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/65f3189c874d62d2.
Report an issue: GitHub.