eshangj/stackoverflow_q_and_a_sample huggingface dataset:
Question: This might be a tough one. Suppose I have a type JSON = Union[Mapping[str, "JSON"], Sequence["JSON"], str, int, float, bool, None]  And I have a function def memoize[**P, T: JSON](fn: Callable[P,T]) -> Callable[P,T]: # ...make the function memoizable return wrapped_fn  How do I constrain the parameters of fn to all be subtypes of JSON? Alternatively, if this can't be done statically, how do I check this inside memoize before creating the wrapper? I tried giving bounds to the ParamSpec variable **P, but it seems this isn't implemented yet. I also tried issubclass but this doesn't play nice with typehints.

Answer: There isn't a way of currently doing this, if fn has an arbitrary signature. I believe that the next best thing is generating errors at the call site (see Pyright playground): import collections.abc as cx import typing as t type JSON = cx.Mapping[str, JSON] | cx.Sequence[JSON] | str | int | float | bool | None class _JSONOnlyCallable(t.Protocol): def __call__(self, /, *args: JSON, **kwargs: JSON) -> JSON: ... def memoize[F: cx.Callable[..., t.Any]](fn: F, /) -> F | _JSONOnlyCallable: return fn @memoize def f(a: int, b: str, c: set[int]) -> str: ...  >>> f(1, "", {1, 2}) ^^^^^^ pyright: Argument of type "set[int]" cannot be assigned to parameter "args" of type "JSON" in function "__call__"
Question: The following is a seemingly simple recursion for finding a hybercube matrix. The recursion is defined as: (Formula) I tried to put it into code but I keep running into broadcasting issues with numpy. I tried instead of taking the size of the previous matrix, just taking powers of 2 to reduce recursive calls (for the parameter of the identity matrix). import numpy as np from numpy import linalg as LA Q1 = np.array([[0, 1], [1, 0]]) def Qn(n): if n <= 1: return Q1 else: return np.array([[Qn(n-1), np.identity(int(np.exp2(n-1)))], [np.identity(int(np.exp2(n-1))), Qn(n-1)]]) Q3= Qn(3) eig_value, eig_vectors = LA.eig(Q3) print(eig_value)  Q1 is the base case of my matrix. It should be very simple, yet I keep running into issues. Traceback (most recent call last): File "e:\Coding\Python\test.py", line 15, in <module> Q3= Qn(3) File "e:\Coding\Python\test.py", line 12, in Qn return np.array([[Qn(n-1), np.identity(int(np.exp2(n-1)))], ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 2 dimensions. The detected shape was (2, 2) + inhomogeneous part.  I get this error ^^

Answer: Use np.block instead of np.array to assemble the block matrix. import numpy as np from numpy import linalg as LA Q1 = np.array([[0, 1], [1, 0]]) def Qn(n): if n <= 1: return Q1 else: Qnm1 = Qn(n-1) I = np.eye(2**(n-1)) return np.block([[Qnm1, I], [I, Qnm1]]) Q3 = Qn(3) eig_value, eig_vectors = LA.eig(Q3) print(eig_value) # [ 3. -3. -1. -1. -1. 1. 1. 1.]
Question: Suppose there are hundreds of columns in a DataFrame. Some of the column names are in lower and some are in upper case. Now, I want to convert all the columns to upper case. import polars as pl df = pl.DataFrame({ "foo": [1, 2, 3, 4, 5, 8], "baz": [5, 4, 3, 2, 1, 9], })  What I tried: df.columns = [x.upper() for x in df.columns]  It worked, but is there any other way preferably without a for loop?

Answer: .rename() also accepts a Callable. df.rename(str.upper)  shape: (6, 2) ┌─────┬─────┐ │ FOO ┆ BAZ │ │ --- ┆ --- │ │ i64 ┆ i64 │ ╞═════╪═════╡ │ 1 ┆ 5 │ │ 2 ┆ 4 │ │ 3 ┆ 3 │ │ 4 ┆ 2 │ │ 5 ┆ 1 │ │ 8 ┆ 9 │ └─────┴─────┘
Question: I'm trying to move away from pandas to polars, I have a use case where I need to add the column totals as a new row to the polars lazy frame. I've found this answer and it would perfectly solve my problem in pandas, but I haven't found any way or documentation on how to translate this code to polars.

Answer: To create a new row with the total of each column, you can first create a row of the column totals using an aggregation and then concatenate it to the dataframe using pl.concat. pl.concat([df, df.select(pl.all().sum())])  Previous answer Disclaimer. My original answer was based on the answer to the linked pandas question. It actually create a new column with the total of each row. To create a new column total that is given by the horizontal sum of all other columns, use pl.sum_horizontal as follows. df.with_columns( pl.sum_horizontal(pl.all()).alias("total") )  If you don't want to sum all columns, give the corresponding column names directly to pl.sum_horizontal, e.g. pl.sum_horizontal("A", "B", "C").
Question: Here are two dataframes grouped how I want them: last5s = pd.Timestamp.now().replace(microsecond=0) - pd.Timedelta('5s') dates = pd.date_range(last5s, periods = 5, freq='s') N=10 data1 = np.random.randint(0,10,N) data2 = np.random.randint(0,10,N) df1 = pd.DataFrame({'timestamp': np.random.choice(dates, size=N), 'A': data1}) df2 = pd.DataFrame({'timestamp': np.random.choice(dates, size=N), 'B': data2}) print(df1) print(df2) print() g1 = df1.groupby(pd.Grouper(key='timestamp', freq='1s')) print("g1:") for time, group in g1: print('time:', time) print(group) print() print() g2 = df2.groupby(pd.Grouper(key='timestamp', freq='1s')) print('g2:') for time, group in g2: print('time:', time) print(group) print()  Output (e.g.):  timestamp A 0 2024-03-01 10:05:26 7 1 2024-03-01 10:05:25 8 2 2024-03-01 10:05:28 1 3 2024-03-01 10:05:24 2 4 2024-03-01 10:05:28 5 5 2024-03-01 10:05:27 4 6 2024-03-01 10:05:24 6 7 2024-03-01 10:05:26 3 8 2024-03-01 10:05:26 8 9 2024-03-01 10:05:28 8 timestamp B 0 2024-03-01 10:05:25 1 1 2024-03-01 10:05:26 6 2 2024-03-01 10:05:25 5 3 2024-03-01 10:05:28 7 4 2024-03-01 10:05:27 7 5 2024-03-01 10:05:28 1 6 2024-03-01 10:05:28 4 7 2024-03-01 10:05:25 0 8 2024-03-01 10:05:24 6 9 2024-03-01 10:05:24 5 g1: time: 2024-03-01 10:05:24 timestamp A 3 2024-03-01 10:05:24 2 6 2024-03-01 10:05:24 6 time: 2024-03-01 10:05:25 timestamp A 1 2024-03-01 10:05:25 8 time: 2024-03-01 10:05:26 timestamp A 0 2024-03-01 10:05:26 7 7 2024-03-01 10:05:26 3 8 2024-03-01 10:05:26 8 time: 2024-03-01 10:05:27 timestamp A 5 2024-03-01 10:05:27 4 time: 2024-03-01 10:05:28 timestamp A 2 2024-03-01 10:05:28 1 4 2024-03-01 10:05:28 5 9 2024-03-01 10:05:28 8 g2: time: 2024-03-01 10:05:24 timestamp B 8 2024-03-01 10:05:24 6 9 2024-03-01 10:05:24 5 time: 2024-03-01 10:05:25 timestamp B 0 2024-03-01 10:05:25 1 2 2024-03-01 10:05:25 5 7 2024-03-01 10:05:25 0 time: 2024-03-01 10:05:26 timestamp B 1 2024-03-01 10:05:26 6 time: 2024-03-01 10:05:27 timestamp B 4 2024-03-01 10:05:27 7 time: 2024-03-01 10:05:28 timestamp B 3 2024-03-01 10:05:28 7 5 2024-03-01 10:05:28 1 6 2024-03-01 10:05:28 4  How do I "join" the groups together such that I can iterate over them together? E.g. I want to be able to do: for time, group1, group2 in somehow_joined(g1,g2): <do stuff with group1 and group2 in this common time group> 

Answer: You can just do: for t, d1 in g1: d2 = g2.get_group(t) if d2 is None: print("I don't want this") continue print(d1) print('-'*10) print(d2) print('='*30)  Output:  timestamp A 0 2024-02-29 19:10:14 0 3 2024-02-29 19:10:14 7 7 2024-02-29 19:10:14 1 ---------- timestamp B 1 2024-02-29 19:10:14 0 3 2024-02-29 19:10:14 6 5 2024-02-29 19:10:14 9 6 2024-02-29 19:10:14 4 ============================== timestamp A 2 2024-02-29 19:10:15 2 5 2024-02-29 19:10:15 8 6 2024-02-29 19:10:15 2 9 2024-02-29 19:10:15 6 ---------- timestamp B 8 2024-02-29 19:10:15 9 ============================== timestamp A 1 2024-02-29 19:10:16 3 4 2024-02-29 19:10:16 9 8 2024-02-29 19:10:16 6 ---------- timestamp B 2 2024-02-29 19:10:16 6 4 2024-02-29 19:10:16 6 ==============================
Question: Dataframe: rng = np.random.default_rng(42) df = pl.DataFrame( { "nrs": [1, 2, 3, None, 5], "names": ["foo", "ham", "spam", "egg", None], "random": rng.random(5), "A": [True, True, False, False, False], } )  Currently, to add a constant to a column I do: df = df.with_columns(pl.col('random') + 500.0)  Questions:  Why does df = df.with_columns(pl.col('random') += 500.0) throw a SyntaxError?  Various AIs tell me that df['random'] = df['random'] + 500 should also work, but it throws the following error instead: TypeError: DataFrame object does not support `Series` assignment by index Use `DataFrame.with_columns`.  Why is polars throwing an error? I've been using df['random'] to identify the random column in other parts of my code, and it worked.  

Answer: AI:s tell you to do so, because they are not actually intelligent They try to suggest you how it's done in pandas, because of similar keywords like dataframe and python. But it just does not work the same way with polars by design. Augmented assignment problem With += too, it's really just a matter of syntax. pl.col is a class (of type <class 'polars.functions.col.ColumnFactory'>), and instantiating that class creates an expression (<Expr ['col("random")'] at 0x701D91A7D850>), but you cannot assign to that the same way as you cannot assign like this before a exists: a += 1  Or more precisely, because you cannot do the same within any function call: >>> a = 10 >>> math.pow(a += 10, 2) File "<stdin>", line 1 math.pow(a += 10, 2) ^^ SyntaxError: invalid syntax >>> math.pow(a + 10, 2) 400.0  Interestingly, you can do this: >>> expr = pl.col("random") >>> expr += 500 >>> expr <Expr ['[(col("random")) + (500)]'] at 0x701D91AE4800> >>> df.select(expr) shape: (5, 1) ┌────────────┐ │ random │ │ --- │ │ f64 │ ╞════════════╡ │ 500.773956 │ │ 500.438878 │ │ 500.858598 │ │ 500.697368 │ │ 500.094177 │ └────────────┘  So this works df = df.with_columns(pl.col('random') + 500.0)  because it's basically equivalent to the previous example, you just create the expression on the same line as you do the with_columns.
Question: I need to insert an element attribute at the correct position using the lxml library. Here is an example where I am trying to insert the attr2 attribute in front of the attr3 attribute: from lxml import etree xml = '<root attr0="val0" attr1="val1" attr3="val3" attr4="val4" attr5="val5"/>' root = etree.fromstring(xml) inse_pos = root.keys().index('attr3') attrib_items = root.items() attrib_items.insert(inse_pos, ('attr2', 'val2')) root.attrib = dict(attrib_items) print(etree.tostring(root))  But I'm getting an error: AttributeError: attribute 'attrib' of 'lxml.etree._Element' objects is not writable

Answer: One possible solution could be to recreate the element attributes from scratch: from lxml import etree xml = '<root attr0="val0" attr1="val1" attr3="val3" attr4="val4" attr5="val5"/>' root = etree.fromstring(xml) attribs = root.attrib.items() root.attrib.clear() for k, v in attribs: if k == "attr3": root.attrib["attr2"] = "val2" root.attrib[k] = v print(etree.tostring(root))  Prints: b'<root attr0="val0" attr1="val1" attr2="val2" attr3="val3" attr4="val4" attr5="val5"/>'
Question: In the __init__.py-file of my project I have the following code, for retrieving the current version of the program from the pyproject.toml-file: from typing import Any from importlib import metadata try: import tomllib with open("pyproject.toml", "rb") as project_file: pyproject: dict[str, Any] = tomllib.load(project_file) __version__ = pyproject["tool"]["poetry"]["version"] except Exception as _: __version__: str = metadata.version(__package__ or __name__)  This code works fine on Python 3.11 and newer. On older versions, however, tomllib does not exist, and therefore the exception should be raised (and the version will be determined by using metadata) When testing on Python 3.11 with pytest, is there any way of designing a test to check if that approach of determining the version works both with and without tomllib, without having to create a second venv without tomllib? I.e. how can I artificially generate the exception, such that I can test both branches without having to switch to different versions?

Answer: You can monkeypatch sys.modules, so tomllib is not there (see Python testing: Simulate ImportError) Here is a curated example: import sys def func(): try: import tomllib return 1 except ImportError: return 0 def test_with_tomllib(): assert func() == 1 def test_without_tomllib(monkeypatch): monkeypatch.setitem(sys.modules, 'tomllib', None) assert func() == 0
Question: Given the code df1 = pl.DataFrame({"A": [1, 1], "B": [3, 4]}) df2 = pl.DataFrame({"A": [1, 1], "C": [5, 6]}) result = df1.join(df2, on='A')  result looks like shape: (4, 3) ┌─────┬─────┬─────┐ │ A ┆ B ┆ C │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 │ ╞═════╪═════╪═════╡ │ 1 ┆ 3 ┆ 5 │ │ 1 ┆ 4 ┆ 5 │ │ 1 ┆ 3 ┆ 6 │ │ 1 ┆ 4 ┆ 6 │ └─────┴─────┴─────┘  but I would like it to be shape: (2, 3) ┌─────┬─────┬─────┐ │ A ┆ B ┆ C │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 │ ╞═════╪═════╪═════╡ │ 1 ┆ 3 ┆ 5 │ │ 1 ┆ 4 ┆ 6 │ └─────┴─────┴─────┘  Experimenting with left_on, right_on and how parameters did not resolve this issue.

Answer: If the tables are aligned (possibly, after sorting by the on column), you can concatenate them horizontally using pl.concat. pl.concat([df1.sort("A"), df2.sort("A").drop("A")], how="horizontal")  shape: (2, 3) ┌─────┬─────┬─────┐ │ A ┆ B ┆ C │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 │ ╞═════╪═════╪═════╡ │ 1 ┆ 3 ┆ 5 │ │ 1 ┆ 4 ┆ 6 │ └─────┴─────┴─────┘  If one table contains more rows and you want to keep the extra rows, you could add an extra column to store the index of the row among all rows with same on column(s). Then, you can join the tables using both on column(s) and the index. ( df1 .with_columns(pl.int_range(pl.len()).over("A").alias("id")) .join( df2.with_columns(pl.int_range(pl.len()).over("A").alias("id")), on=["A", "id"], how="left", ) .select(pl.exclude("id")) )
Question: I have a dataframe that I am exporting to Excel. I would also like to style it before the export. I have this code which changes the background color and text color and works fine, but I would like to add to it: df.style.set_properties(**{'background-color': 'black', 'color': 'lawngreen', 'border-color': 'white'}).to_excel(writer, sheet_name='Sheet1', startrow=rowPos, float_format = "%0.5f")  I need columns with strings and dates to have a white text color, and then positive numbers to be green and negative numbers to be red. I pulled these styles directly from pandas documentation on styling since I have never used it before, and am unsure how to achieve these results. Lets say my dataframe looks like this: StartDate ExpiryDate Commodity Quantity Price Total --------- ---------- ---------- ------- ----- ----- 02/28/2024 12/28/2024 HO 10000 -3.89 -38900 02/28/2024 12/28/2024 WPI 10000 4.20 42000  how could I achieve what I am looking for?

Answer: I'd break it down into three steps (see the comments #) : st = ( df.style # 1-applying the default styles .set_properties(**default_css) # 2-formatting the numeric columns .apply( lambda df_: df_.select_dtypes("number") .lt(0).replace({True: tc(neg), False: tc(pos)}), axis=None, ) .format(precision=2) # this one is optional # 3-formatting the string-like dates and strings .map(lambda v: tc(obj) if isinstance(v, str) else "") ) # st.to_excel("output.xlsx", index=False) # uncomment to make an Excel  Output :  Used CSS : default_css = { "background-color": "black", "border": "1px solid white", } tc = "color: {}".format # css text color obj, pos, neg = "white", "lawngreen", "red"
Question: I have a class that accepts either ints or floats in it's init but all must be int or float so i am using typing.overload to achieve that and I want to be able to type hint the return of a function based on the given values. class Vector3: @overload def __init__(self, x: int, y: int, z: int) -> None: ... @overload def __init__(self, x: float, y: float, z: float) -> None: ... def __init__(self, x, y, z) -> None: self._x = x self._y = y self._z = z # This function def __key(self) -> tuple[int | float, int | float, int | float]: return (self._x, self._y, self._z)  Also how would I type hint the values of x, y, and z? I plan to use @property to obfuscate the _x, _y, _z values and don't know how I'd type hint them either. @property def x(self) -> int | float: return self._x 

Answer: Don't overload __init__ at all. Make Vector3 generic instead, using a constrained type variable. from typing import Generic, TypeVar T = TypeVar('T', int, float) class Vector3(Generic[T]): def __init__(self, x: T, y: T, z: T) -> None: self._x: T = x self._y: T = y self._z: T = z def __key(self) -> tuple[T, T, T]: return (self._x, self._y, self._z) @property def x(self) -> T: return self._x # etc  Then reveal_type(Vector3(1, 2, 3)) # Vector3[int] reveal_type(Vector3(1., 2., 3.)) # Vector3[float] reveal_type(Vector3(1, 2, 3.)) # Vector3[float], via promotion of int values to floats reveal_type(Vector3('1', '2', '3')) # type error, T cannot be bound to str  Note that int is considered a subtype of float in this context.
Question: import polars as pl import sqlite3 conn = sqlite3.connect("test.db") df = pl.DataFrame({"col1": [1, 2, 3]})  According to the documentation of pl.write_database, I need to pass a connection URI string e.g. "sqlite:////path/to/database.db" for SQLite database: df.write_database("test_table", f"sqlite:////test.db", if_table_exists="replace")  However, I got the following error: OperationalError: (sqlite3.OperationalError) unable to open database file  EDIT: Based on the answer, install SQLAlchemy with the pip install polars[sqlalchemy] command.

Answer: When working with SQLite, the number of slashes depends on how you are accessing the sqlite file. If you use two slashes as suggested in the comments, you'll see this: sqlalchemy.exc.ArgumentError: Invalid SQLite URL: sqlite://test.db Valid SQLite URL forms are: sqlite:///:memory: (or, sqlite://) sqlite:///relative/path/to/file.db sqlite:////absolute/path/to/file.db  It appears that in your case, you want to use 3 slashes and not 4. Please try this instead: df.write_database("test_table", f"sqlite:///test.db", if_table_exists="replace")  This would be a working example, based on the sample code snippets you provided: import polars as pl import sqlite3 conn = sqlite3.connect("test.db") df = pl.DataFrame({"col1": [1, 2, 3]}) df.write_database("test_table", f"sqlite:///test.db", if_table_exists="replace")
Question: Example: import gradio as gr def dummy(a, b='Hello', c='!'): return '{0} {1} {2}'.format(b, a, c) with gr.Blocks() as demo: txt = gr.Textbox(value="test", label="Query", lines=1) txt2 = gr.Textbox(value="test2", label="Query2", lines=1) answer = gr.Textbox(value="", label="Answer") btn = gr.Button(value="Submit") btn.click(dummy, inputs=[txt], outputs=[answer]) gr.ClearButton([answer]) demo.launch()   How can I have the value of txt2 be given to the dummy()'s named parameter c?

Answer: As far as I know, Gradio doesn't directly allow it. I.e., it doesn't allow something like this: btn.click(fn=dummy, inputs={"a": txt, "c": txt2}, outputs=answer)  In this specific case, if you can't modify the dummy function then I would rearrange the parameters with the following function: def rearrange_args(func): def wrapper(*args): a, c = args return func(a, c=c) return wrapper  Now, you just need to modify your example as follows: btn.click(rearrange_args(dummy), inputs=[txt, txt2], outputs=[answer])
Question: How to gather all the values that repeated the most in each row of the numpy array, like the result of np.unique. However, I want to avoid loops as the data to be handle will be much larger (much more rows). See the example below Input: a is a 2D array, had the shape of (x, k), where x would be very large. a = np.asarray([[2, 7, 7, 2, 1], [1, 2, 3, 5, 5], [6, 6, 6, 6, 6]]) Ideal output: [[2,7], [5], [6]], first row had [2,7] both exist in 2 times Using loops can nearly do the job, but np.unique seems not working the same for multi-dimension arrays [np.array(np.unique(i, return_counts=1)) for i in a] # decent output > [array([[1, 2, 7],[1, 2, 2]], dtype=int64), array([[1, 2, 3, 5], [1, 1, 1, 2]], dtype=int64), array([[6], [5]], dtype=int64)] # Multi-dimension Input np.unique(a, return_counts=1, axis=1) # Useless Output > (array([[1, 2, 2, 7, 7], [5, 1, 5, 2, 3], [6, 6, 6, 6, 6]]), array([1, 1, 1, 1, 1], dtype=int64)) 

Answer: Use statistics.multimode: from statistics import multimode out = list(map(multimode, a))  Output: [[2, 7], [5], [6]]
Question: I would like to add multiple columns at once to a Polars dataframe, where each column derives from the same object (for a row), by creating the object only once and then returning a method of that object for each column. Here is a simplified example using a range object: import polars as pl df = pl.DataFrame({ 'x': [11, 22], }) def uses_object(x): r = list(range(0, x)) c10 = r.count(10) c12 = r.count(12) return c10, c12 df = df.with_columns( count_of_10 = pl.col('x').map_elements(lambda x: uses_object(x)[0]), count_of_12 = pl.col('x').map_elements(lambda x: uses_object(x)[1]), ) print(df)  shape: (2, 3) ┌─────┬─────────────┬─────────────┐ │ x ┆ count_of_10 ┆ count_of_12 │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 │ ╞═════╪═════════════╪═════════════╡ │ 11 ┆ 1 ┆ 0 │ │ 22 ┆ 1 ┆ 1 │ └─────┴─────────────┴─────────────┘  I tried multiple assignment df = df.with_columns( count_of_10, count_of_12 = uses_object(pl.col('x')), )  but got error NameError name 'count_of_10' is not defined.  Can I change the code to call uses_object only once?

Answer: If you return a dictionary from your function: return dict(count_of_10=c10, count_of_12=c12)  You will get a struct column: df.with_columns( count = pl.col('x').map_elements(uses_object) )  shape: (2, 2) ┌─────┬───────────┐ │ x ┆ count │ │ --- ┆ --- │ │ i64 ┆ struct[2] │ ╞═════╪═══════════╡ │ 11 ┆ {1,0} │ │ 22 ┆ {1,1} │ └─────┴───────────┘  Which you can .unnest() into individual columns. df.with_columns( count = pl.col('x').map_elements(uses_object) ).unnest('count')  shape: (2, 3) ┌─────┬─────────────┬─────────────┐ │ x ┆ count_of_10 ┆ count_of_12 │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 │ ╞═════╪═════════════╪═════════════╡ │ 11 ┆ 1 ┆ 0 │ │ 22 ┆ 1 ┆ 1 │ └─────┴─────────────┴─────────────┘  As for your current approach, you would call it once and then use Polars list methods to extract the values in a separate .with_columns / .select e.g. df.with_columns( count = pl.col('x').map_elements(uses_object) ).with_columns( count_of_10 = pl.col('count').list.first(), count_of_12 = pl.col('count').list.last(), ).drop('count')
Question: This is my DataFrame: import pandas as pd df = pd.DataFrame( { 'a': [2, 2, 2, -4, 4, 4, 4, -3, 2, -2, -6], 'b': [2, 2, 2, 4, 4, 4, 4, 3, 2, 2, 6] } )  I use a function to highlight cells in a when I use to_excel: def highlight_cells(s): if s.name=='a': conds = [s > 0, s < 0, s == 0] labels = ['background-color: lime', 'background-color: pink', 'background-color: gold'] array = np.select(conds, labels, default='') return array else: return ['']*s.shape[0]  Now I want to add one more feature by adding plus sign if a value in a is positive. For example 1 becomes +1. I want this feauture only for column a. This is my attempt but it does not work. It gives me the error that is the title of the post. df.style.apply(highlight_cells).style.format({'a': '{:+g}'}).to_excel('df.xlsx', sheet_name='xx', index=False) 

Answer: style.apply already returns a Styler object, you only need further do operation on this, i.e. df.style.apply(highlight_cells).format(...) # ^ # | # No need .style again