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_dict

View on GitHub (pinned to 79633dd950)

Solutions

  1. Pass a single column name as field (a str like "$close") so self.data[stock_id][field] is a pd.Series
  2. Check quote_df for duplicated column names: quote_df.columns[quote_df.columns.duplicated()] and drop duplicates before constructing PandasQuote
  3. Use only documented methods: None, "last", "all", "sum", "mean", "ts_data_last"
  4. 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

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


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/65f3189c874d62d2. Report an issue: GitHub.