{"record":{"id":"65f3189c874d62d2","repo":"microsoft/qlib","slug":"stock-data-from-resam-ts-data-must-be-a-number-pd","errorCode":null,"errorMessage":"stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame","messagePattern":"stock data from resam_ts_data must be a number, pd\\.Series or pd\\.DataFrame","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/backtest/high_performance_ds.py","lineNumber":125,"sourceCode":"        for stock_id, stock_val in quote_df.groupby(level=\"instrument\", group_keys=False):\n            quote_dict[stock_id] = stock_val.droplevel(level=\"instrument\")\n        self.data = quote_dict\n\n    def get_all_stock(self):\n        return self.data.keys()\n\n    def get_data(self, stock_id, start_time, end_time, field, method=None):\n        if method == \"ts_data_last\":\n            method = ts_data_last\n        stock_data = resam_ts_data(self.data[stock_id][field], start_time, end_time, method=method)\n        if stock_data is None:\n            return None\n        elif isinstance(stock_data, (bool, np.bool_, int, float, np.number)):\n            return stock_data\n        elif isinstance(stock_data, pd.Series):\n            return idd.SingleData(stock_data)\n        else:\n            raise ValueError(f\"stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame\")\n\n\nclass NumpyQuote(BaseQuote):\n    def __init__(self, quote_df: pd.DataFrame, freq: str, region: str = \"cn\") -> None:\n        \"\"\"NumpyQuote\n\n        Parameters\n        ----------\n        quote_df : pd.DataFrame\n            the init dataframe from qlib.\n        self.data : Dict(stock_id, IndexData.DataFrame)\n        \"\"\"\n        super().__init__(quote_df=quote_df, freq=freq)\n        quote_dict = {}\n        for stock_id, stock_val in quote_df.groupby(level=\"instrument\", group_keys=False):\n            quote_dict[stock_id] = idd.MultiData(stock_val.droplevel(level=\"instrument\"))\n            quote_dict[stock_id].sort_index()  # To support more flexible slicing, we must sort data first\n        self.data = quote_dict","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/backtest/high_performance_ds.py#L107-L143","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nquote.get_data(\"SH600000\", \"2010-01-04\", \"2010-01-06\", field=[\"$close\", \"$volume\"])\n# after\nfor f in (\"$close\", \"$volume\"):\n    quote.get_data(\"SH600000\", \"2010-01-04\", \"2010-01-06\", field=f)","handlingStrategy":"validation","validationCode":"# before calling get_data, confirm the field selects exactly one Series column\ncols = quote_df.columns\nassert isinstance(field, str) and (cols == field).sum() == 1, f\"field {field!r} must match exactly one column\"","typeGuard":"def is_single_field(quote_df, field) -> bool:\n    import pandas as pd\n    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)","tryCatchPattern":"try:\n    val = quote.get_data(sid, t0, t1, field)\nexcept ValueError as e:\n    if \"must be a number, pd.Series or pd.DataFrame\" in str(e):\n        raise TypeError(f\"field {field!r} selected multiple columns; pass one field at a time\") from e\n    raise","preventionTips":["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'}"],"tags":["python","qlib","pandas","type-error","backtest"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}