eshangj/stackoverflow_q_and_a_sample huggingface dataset:
Question: I have a callback that takes the result of another function as input. Is there a way to directly reference that function's return type? Currently I have the return type defined as a type alias that I can use in both places but that doesn't seem ideal. Does something like C++'s std::result_of exist in python? Current Code from typing import Tuple func_return = Tuple[int, int] def func() -> func_return: return 1, 1 def call_back(arg: func_return) -> int: a, b = arg return a + b  Ideal Code from typing import Tuple def func() -> Tuple[int, int]: return 1, 1 def call_back(arg: return_type(func)) -> int: a, b = arg return a + b 

Answer: No, because call_back only knows/cares about the value, not which function produces the value. call_back takes a value, and it's the caller's responsibility to provide a value of the correct type, whether or not they use a function to produce that value. Focus on the type, not the functions that produce or accept values of that type. IntPair = Tuple[int, int] def func() -> IntPair: return 1, 1 def call_back(arg: IntPair) -> int: a, b = arg return a + b  (Same as your current code, but with a name that focuses on what the type is, not where it might come from: # All legal, all equivalent call_back(func()) call_back((1, 1)) call_back((1,) + (1,))  )
Question: could you please advice how to add Quater column, contains sum of months values    part 2024-03-01 00:00:00 2024-04-01 00:00:00 2024-05-01 00:00:00 2024-06-01 00:00:00 2024-07-01 00:00:00 2024-08-01 00:00:00 2024-09-01 00:00:00     part1 6 8 2 3 0 5 5   part2 7 1 3 8 9 4 10   part3 10 7 4 5 6 10 0   part4 6 9 3 0 10 9 10   part5 2 1 10 8 7 3 3   part6 1 0 4 1 1 7 8    I'm tring to get output like this:    part 2024-03-01 00:00:00 Q1'24 2024-04-01 00:00:00 2024-05-01 00:00:00 2024-06-01 00:00:00 Q2'24 2024-07-01 00:00:00 2024-08-01 00:00:00 2024-09-01 00:00:00 Q3'24     part1 6 6 8 2 3 13 0 5 5 10   part2 7 7 1 3 8 12 9 4 10 23   part3 10 10 7 4 5 16 6 10 0 16   part4 6 6 9 3 0 12 10 9 10 29   part5 2 2 1 10 8 19 7 3 3 13   part6 1 1 0 4 1 5 1 7 8 16    raw data

Answer: You can convert columns to quarter periods by DatetimeIndex.to_period for subtotals, join to original by concat and for correct order add DataFrame.sort_index: df.columns = pd.to_datetime(df.columns) out = (pd.concat([df, df.groupby(df.columns.to_period('Q'), axis=1).sum()], axis=1) .sort_index(axis=1, key=lambda x: pd.PeriodIndex(x, 'Q'))) out.columns = [x.strftime("Q%q'%y") if isinstance(x, pd.Period) else x for x in out.columns]   print (out) 2024-03-01 00:00:00 Q1'24 2024-04-01 00:00:00 2024-05-01 00:00:00 \ part part1 6 6 8 2 part2 7 7 1 3 part3 10 10 7 4 part4 6 6 9 3 part5 2 2 1 10 part6 1 1 0 4 2024-06-01 00:00:00 Q2'24 2024-07-01 00:00:00 2024-08-01 00:00:00 \ part part1 3 13 0 5 part2 8 12 9 4 part3 5 16 6 10 part4 0 12 10 9 part5 8 19 7 3 part6 1 5 1 7 2024-09-01 00:00:00 Q3'24 part part1 5 10 part2 10 23 part3 0 16 part4 10 29 part5 3 13 part6 8 16  Good point - in last version of pandas (2.1.4+) need transpose, thank you @nick: df.columns = pd.to_datetime(df.columns) df = df.T out = (pd.concat([df, df.groupby(df.index.to_period('Q')).sum()]).T .sort_index(axis=1, key=lambda x: pd.PeriodIndex(x, 'Q')) ) out.columns = [x.strftime("Q%q'%y") if isinstance(x, pd.Period) else x for x in out.columns] print (out)    2024-03-01 00:00:00 Q1'24 2024-04-01 00:00:00 2024-05-01 00:00:00 \ part part1 6 6 8 2 part2 7 7 1 3 part3 10 10 7 4 part4 6 6 9 3 part5 2 2 1 10 part6 1 1 0 4 2024-06-01 00:00:00 Q2'24 2024-07-01 00:00:00 2024-08-01 00:00:00 \ part part1 3 13 0 5 part2 8 12 9 4 part3 5 16 6 10 part4 0 12 10 9 part5 8 19 7 3 part6 1 5 1 7 2024-09-01 00:00:00 Q3'24 part part1 5 10 part2 10 23 part3 0 16 part4 10 29 part5 3 13 part6 8 16
Question: I want to create a script that recreates a chromadb collection - delete previous version and creates a new from scratch. client.delete_collection(name=COLLECTION_NAME) collection = client.create_collection( name=COLLECTION_NAME, embedding_function=embedding_func, metadata={"hnsw:space": "cosine"}, )  However, if the collection does not exist, I receive error:  File "*/lib/python3.11/site-packages/chromadb/api/segment.py", line 347, in delete_collection raise ValueError(f"Collection {name} does not exist.")  ValueError: Collection operations does not exist. Is there any command to check if a collection exists? I haven't found any in documentation.

Answer: You can use the following function  collection = client.get_or_create_collection(name="test")  It either gets the collection or creates it
Question: Suppose we have a dictionary d defined as follows d = {i:i for i in range(1,1000001)}  And then we store d.items() in x. So, x is a collection of tuples that contain key-value pairs for each element of d. I made a list with the same tuples, defined as follows : l = [(i, i) for i in range(1,1000001)]  Now, I compared the time taken to execute (1000001, 1000001) in x and (1000001, 1000001) in l. The time difference was very significant. I assumed that this was because x had a set-like behavior and implemented hash tables on tuples, as tuples are hashable as well. However, tuples that contain lists are non hashable, so I tried the same thing with this dictionary : d = {i:[i,i+1] for i in range(1,1000001)}  Unlike what I expected, it still enabled fast lookup. How does this work? PS : Here's my code for time comparison import time def for_dict_items(n): #d = {i:i for i in range(1, n+1)} d = {i:[i, i+1] for i in range(1, n+1)} di = d.items() st = time.time() x = (n, [n, n+1]) in di et = time.time() return (et - st) def for_tuples_list(n): #l = [(i, i) for i in range(1, n+1)] l = [(i,[i, i+1]) for i in range(1, n+1)] st = time.time() x = (n, [n, n+1]) in l et = time.time() return (et - st) k = 1000000 t1 = for_dict_items(k) t2 = for_tuples_list(k) print(t1, t2, t2/t1, sep = "\n") 

Answer: Since dict is one of the most important core objects, it's implemented in C, as well as dict_values, dict_keys and dict_items. Here's the CPython implementation of dictitems_contains: static int dictitems_contains(PyObject *self, PyObject *obj) { _PyDictViewObject *dv = (_PyDictViewObject *)self; int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); result = PyDict_GetItemRef((PyObject *)dv->dv_dict, key, &found); if (result == 1) { result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); } return result; }  If you don't speak C, here's what it does:  Check that view is correct - namely that it has a reference to the underlying dictionary (and return False if not, for some reason) Check the input: if it's not a 2-tuple, return False. Look up a key in the underlying dictionary. If found, check if value matches. If it does, return True. Otherwise, return False.  The core of this is the fact that dict.items returns not a list, but a special dict_items view. The term view here means that it does not represent a copy, but only a proxy to retrieve underlying items in some specific way. The same applies to dict_keys and dict_values, you can read more in the excellent documentation page on this topic. Note that this is specific to CPython implementation and may be wrong for other implementations.
Question: I am struggling to understand how pd.concat works when the input is a dictionary. Let's say we have the following pandas dataframe - # Import pandas library import pandas as pd # initialize list of lists data = [['tom', 10], ['nick', 15], ['juli', 14]] # Create the pandas DataFrame df = pd.DataFrame(data, columns=['Name', 'Age'])  Then, we do the following concatenation operation - z = pd.concat({"z":df}, axis = 1) print(z)  The output comes out to be -  z Name Age 0 tom 10 1 nick 15 2 juli 14  It seems like the key z was stacked on top of the dataframe df. But this doesn't make sense as the axis specified was 1 and therefore, the stacking (if that's what occurred) should've been across columns.

Answer: It actually makes sense, since you concatenate as columns (axis=1) you need to differentiate the concatenated columns. Here is a more meaningful example: out = pd.concat({'left': df.add_prefix('left_'), 'middle': df.add_prefix('middle_'), 'right': df.add_prefix('right_')}, axis=1) left middle right left_Name left_Age middle_Name middle_Age right_Name right_Age 0 tom 10 tom 10 tom 10 1 nick 15 nick 15 nick 15 2 juli 14 juli 14 juli 14  This is equivalent to passing the new names to keys: out = pd.concat([df.add_prefix('left_'), df.add_prefix('middle_'), df.add_prefix('right_')], keys=['left', 'middle', 'right'], axis=1)  If you were concatenating on axis=0 (rows), then concat would prefix an index level: out = pd.concat({'top': df, 'middle': df, 'bottom': df}, axis=0) Name Age top 0 tom 10 1 nick 15 2 juli 14 middle 0 tom 10 1 nick 15 2 juli 14 bottom 0 tom 10 1 nick 15 2 juli 14
Question: I want to programmatically list the name and last modification time of every file in a certain revision. Running git log for every file, as suggested here is very slow. Is there a faster way to accomplish this? Running the script below on a non-trivial repo (SDL) takes 59s on my machine. #!/usr/bin/env python import datetime import subprocess import time commit = "HEAD" start = time.time() file_names = subprocess.check_output(["git", "ls-tree", "--name-only", "-r", commit], text=True).strip().split("\n") print(f"[{time.time() - start:.4f}] git ls-tree finished") file_times = list(datetime.datetime.fromisoformat(subprocess.check_output(["git", "log", "-1", "--pretty=format:%cI", commit, "--", name], text=True).strip()) for name in file_names) print(f"[{time.time() - start:.4f}] git info finished") 

Answer: The basic idea is to postprocess git log --name-status with whatever per-commit info you want and look for the first occurrence of names you're interested in. The all-of-them version:  git log --name-status --pretty=%ci | awk -F$'\t' ' NF==1 { stamp=$0; next } !seen[$2]++ { print stamp,$0 } ' | sort -t$'\t' -k2,2  and as always season to taste. Are you running on spinning rust? I do that on the SDL default checkout with a cheap ssd it takes 0.548s, so more than a hundred times faster. But then, it's doing 1500+ times fewer walks through history so there's that.
Question: Here's my example: In [1]: import pandas as pd In [2]: df = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6]}) In [3]: df1 = df.rename(columns={'b': 'b'}, copy=False) In [4]: df1.isetitem(1, [7,8,9]) In [5]: df Out[5]: a b 0 1 4 1 2 5 2 3 6 In [6]: df1 Out[6]: a b 0 1 7 1 2 8 2 3 9  If df1 was derived from df with copy=False, then I'd have expected an in-place modification of df1 to also affect df. But it doesn't. Why? I'm using pandas version 2.2.1, with no options (e.g. copy-on-write) enabled

Answer: copy=False means that the underlying data (numpy) is shared. If we modify one item of the underlying numpy array, it is reflected since the data is shared: df = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6]}) df1 = df.rename(columns={'b': 'c'}, copy=False) df1.values[0, 1] = 999 print(df) a b 0 1 999 1 2 5 2 3 6 print(df1) a c 0 1 999 1 2 5 2 3 6 print(id(df.values), id(df1.values)) # 140335530654768 140335530654768  The DataFrame objects (containers, indices, column names…) are however not identical. print(id(df), id(df1)) # 140335696175520 140335696171536  By setting a new column with df1.isetitem(1, [7,8,9]) you just add a new Series and force a copy.
Question: I have 2 DataFrames: import polars as pl df1 = pl.DataFrame( { "group": ["A", "A", "A", "B", "B", "B"], "index": [1, 3, 5, 1, 3, 8], } ) df2 = pl.DataFrame( { "group": ["A", "A", "A", "B", "B", "B"], "index": [3, 4, 7, 2, 7, 10], } )  I want to cap the index in df2 using the largest index of each group in df1. The groups in two DataFrames are the same. expected output for df2: shape: (6, 2) ┌───────┬───────┐ │ group ┆ index │ │ --- ┆ --- │ │ str ┆ i64 │ ╞═══════╪═══════╡ │ A ┆ 3 │ │ A ┆ 4 │ │ A ┆ 5 │ │ B ┆ 2 │ │ B ┆ 7 │ │ B ┆ 8 │ └───────┴───────┘ 

Answer: You can compute the max per group over df1, then clip df2: out = df2.with_columns( pl.col('index').clip( upper_bound=df1.select(pl.col('index').max().over('group'))['index'] ) )  Output: shape: (6, 2) ┌───────┬───────┐ │ group ┆ index │ │ --- ┆ --- │ │ str ┆ i64 │ ╞═══════╪═══════╡ │ A ┆ 3 │ │ A ┆ 4 │ │ A ┆ 5 │ │ B ┆ 2 │ │ B ┆ 7 │ │ B ┆ 8 │ └───────┴───────┘  Alternatively, if the two groups are not necessarily the same in both dataframes, you could group_by.max then align with join: df1 = pl.DataFrame( { "group": ["A", "A", "A", "B", "B", "B"], "index": [1, 3, 5, 1, 3, 7], } ) df2 = pl.DataFrame( { "group": ["A", "A", "A", "B", "B", "B", "B"], "index": [3, 4, 7, 2, 7, 8, 9], } ) out = df2.with_columns( pl.col('index').clip( upper_bound=df2.join(df1.group_by('group').max(), on='group')['index_right'] ) )  Output: shape: (7, 2) ┌───────┬───────┐ │ group ┆ index │ │ --- ┆ --- │ │ str ┆ i64 │ ╞═══════╪═══════╡ │ A ┆ 3 │ │ A ┆ 4 │ │ A ┆ 5 │ │ B ┆ 2 │ │ B ┆ 7 │ │ B ┆ 7 │ │ B ┆ 7 │ └───────┴───────┘
Question: I'm trying to find out how exactly importing works in Python. Let's say I have a foo.py module that contains the following class: class Foo: def __init__(self, *args, **kwargs): ... foo = Foo()  Now, I want to import it in other modules. Is it going to use the same instance every time that I import it or will make different instances whenever I import it and is it even a good way to instantiate a class or it'd be better to import the Foo class and create an instance in modules that imported it(As it's obvious, this class doesn't need any args and kwargs).

Answer: Import it in the usual way: from foo import Foo If this is the first time, the foo.py code will execute and the {class, def} statements will generate bytecode for the interpreter. And we make a note of it in sys.modules. Subsequent imports will benefit from a cache hit, so we won’t repeat all that evaluation work. When baz.py and qux.py ask for an import of the foo module, they will immediately obtain the cached result, which contains that same old Foo class bytecode. Bottom line: you get exactly one instance of the code in that class.
Question: Another question was asking about the correct way of getting magnitude and phase spectra while using cv2.dft. My answer was limited to the numpy approach and then I thought that using OpenCV for this would be even nicer. I am currently trying to reproduce the same results but I am seeing significant differences in the phase spectrum. Here are my imports: %matplotlib notebook import matplotlib.pyplot as plt import numpy as np import cv2 im = np.zeros((50, 50), dtype = np.float32) # create empty array im[2:10, 2:10] = 255 # draw a rectangle  The numpy example and results:  imFFTNumpy = np.fft.fft2(im) imFFTNumpyShifted = np.fft.fftshift(imFFTNumpy) magSpectrumNumpy = np.abs(imFFTNumpyShifted) phaseSpectrumNumpy = np.angle(imFFTNumpyShifted) fig, ax = plt.subplots(nrows = 1, ncols = 3) ax[0].imshow(im) ax[1].imshow(magSpectrumNumpy) ax[2].imshow(phaseSpectrumNumpy) plt.suptitle("Using Numpy np.fft.fft2 and np.abs/ np.angle")   The OpenCV example and results: imFFTOpenCV = cv2.dft(im, flags=cv2.DFT_COMPLEX_OUTPUT) imFFTOpenCVShifted = np.fft.fftshift(imFFTOpenCV) magSpectrumOpenCV, phaseSpectrumOpenCV = cv2.cartToPolar(imFFTOpenCVShifted[:,:,0], imFFTOpenCVShifted[:,:,1]) fig, ax = plt.subplots(nrows = 1, ncols = 3) ax[0].imshow(im) ax[1].imshow(magSpectrumOpenCV) ax[2].imshow(phaseSpectrumOpenCV) plt.suptitle("Using OpenCV cv2.dft and cv2.cartToPolar")   As you can see, while the magnitude spectrum looks the same (it has some expected deviations due to floating-point arithmetic), the phase spectrum looks significantly different. I dug around a bit and found out that OpenCV usually returns phase from 0 to 2π, whereas np.angle returns the phase from -π to +π. Subtracting π from the OpenCV phase does not correct difference though. What could be the reason for this? Is it possible to get almost identical phase using both approaches, just like with magnitude?

Answer: Given that imFFTOpenCV is a 3D array because OpenCV doesn’t understand complex numbers, np.fft.fftshift(imFFTOpenCV) will swap the real and complex planes. That is, the shift happens in all 3 dimensions of the array. So when computing the phase and magnitude, you need to take this swap into account: magSpectrumOpenCV, phaseSpectrumOpenCV = cv2.cartToPolar(imFFTOpenCVShifted[:,:,1], imFFTOpenCVShifted[:,:,0])  Alternatively, and this is probably more readable, you could tell NumPy which axes you want shifted: imFFTOpenCVShifted = np.fft.fftshift(imFFTOpenCV, axes=(0, 1))
Question: This is my DataFrame: import pandas as pd df = pd.DataFrame( { 'a': [100, 1123, 123, 100, 1, 0, 1], 'b': [1000, 11123, 1123, 0, 55, 0, 1], }, index=range(100, 107) )  And this is the expected output. I want to create column c:  a b c 100 100 1000 NaN 101 1123 11123 NaN 102 123 1123 NaN 103 100 0 3.0 104 1 55 NaN 105 0 0 NaN 106 1 1 NaN  The mask that is used is: mask = ((df.a > df.b))  I want to get the index of first row that mask occurs. I want to preserve the original index but get the reset_index() value. In this example the first instance of the mask is at index 3. I can get the first instance of the mask by this: df.loc[mask.cumsum().eq(1) & mask, 'c'] = 'the first row'  But I don't know how to get the index.

Answer: Code This code can be modified to search for the second and third items as well, not only first. cond1 = df['a'] > df['b'] cond2 = df.groupby(cond1).cumcount().eq(0) df.loc[cond1 & cond2, 'c'] = 'the first row'  df:  a b c 100 100 1000 NaN 101 1123 11123 NaN 102 123 1123 NaN 103 100 0 the first row 104 1 55 NaN 105 0 0 NaN 106 1 1 NaN  If you are only looking for the first value, the following code may be simpler: df.loc[df['a'].gt(df['b']).cummax().cumsum().eq(1), 'c'] = 'the first row'   Updete Answer if you want only location of index, use following code: cond1 = df['a'] > df['b'] idx = cond1.idxmax() loc = df.index.get_loc(idx)  loc: 3   df.loc[df.index == idx, 'c'] = loc  df:  a b c 100 100 1000 NaN 101 1123 11123 NaN 102 123 1123 NaN 103 100 0 3 104 1 55 NaN 105 0 0 NaN 106 1 1 NaN
Question: I am trying to write a Polars DataFrame to a duckdb database. I have the following simple code which I expected to work: import polars as pl import duckdb pldf = pl.DataFrame({'mynum': [1,2,3,4]}) with duckdb.connect(database="scratch.db", read_only=False) as con: pldf.write_database(table_name='test_table', connection=con)  However, I get the following error: sqlalchemy.exc.ArgumentError: Expected string or URL object, got <duckdb.duckdb.DuckDBPyConnection object  I get a similar error if I use the non-default engine='adbc' instead of df.write_database()'s default engine='sqlalchemy'. So it seemed it should be easy enough to just swap in a URI for my ducdkb database, but I haven't been able to get that to work either. Potentially it's complicated by my being on Windows?

Answer: In-memory database. If you just want to use DuckDB to query a polars dataframe, this can simply be achieved as long as the table exists in the current scope. duckdb.sql("SELECT * FROM df").show()  Persistent database If you want to use a persistent database, you could install duckdb-engine and write the database using the connection URI string. df.write_database( table_name='test_table', connection="duckdb:///scratch.db", )  Reading the data back in using DuckDB works as usual. with duckdb.connect(database="scratch.db", read_only=False) as con: con.query("SELECT * FROM test_table").show()  ┌───────┐ │ mynum │ │ int64 │ ├───────┤ │ 1 │ │ 2 │ │ 3 │ │ 4 │ └───────┘
Question: I'm working on a project where I need to create a new DataFrame based on an existing one, with certain columns randomly selected and assigned in each row with probability proportional to the number in that column. However, my current implementation seems to be inefficient, especially when dealing with large datasets. I'm seeking advice on how to optimize this process for better performance. Here's a simplified version of what I'm currently doing: import pandas as pd import numpy as np # Sample DataFrame data = { 'dog': [1, 2, 3, 4], 'cat': [5, 6, 7, 8], 'parrot': [9, 10, 11, 12], 'owner': ['fred', 'bob', 'jim', 'jannet'] } df = pd.DataFrame(data) # List of relevant columns relevant_col_list = ['dog', 'cat', 'parrot'] # New DataFrame with the same number of rows new_df = df.copy() # Create 'iteration_1' column in new_df new_df['iteration_1'] = "" # Iterate over rows for index, row in new_df.iterrows(): # Copy columns not in relevant_col_list for column in new_df.columns: if column not in relevant_col_list and column != 'iteration_1': new_df.at[index, column] = row[column] # Randomly select a column from relevant_col_list with probability proportional to the number in the column probabilities = df[relevant_col_list ].iloc[index] / df[relevant_col_list ].iloc[index].sum() chosen_column = np.random.choice(relevant_col_list , p=probabilities) # Write the name of the chosen column in the 'iteration_1' column new_df.at[index, 'iteration_1'] = chosen_column print(new_df)  How can I speed it up?

Answer: You could first rework your DataFrame to select the columns of interest, normalize the weights, then create a cumsum. tmp = (df[relevant_col_list] .pipe(lambda x: x.div(x.sum(axis=1), axis=0)) .cumsum(axis=1).to_numpy() ) # cumulated probabilities array([[0.06666667, 0.4 , 1. ], [0.11111111, 0.44444444, 1. ], [0.14285714, 0.47619048, 1. ], [0.16666667, 0.5 , 1. ]])  After that generate n random numbers between 0-1 and identify the first column greater than the random number with argmax: r = np.random.random(len(df)) df['iteration_1'] = np.array(relevant_col_list)[(tmp > r[:,None]).argmax(axis=1)]  Output:  col1 col2 col3 col4 iteration_1 0 1 5 9 13 col3 1 2 6 10 14 col3 2 3 7 11 15 col1 3 4 8 12 16 col2  Intermediates: # for reproducibility np.random.seed(1) tmp # array([[0.06666667, 0.4 , 1. ], # [0.11111111, 0.44444444, 1. ], # [0.14285714, 0.47619048, 1. ], # [0.16666667, 0.5 , 1. ]]) r = np.random.random(len(df)) # array([0.417022005, 0.720324493, 0.114374817, 0.302332573]) (tmp > r[:,None]) # array([[False, False, True], # [False, False, True], # [ True, True, True], # [False, True, True]]) (tmp > r[:,None]).argmax(axis=1) # array([2, 2, 0, 1]) np.array(relevant_col_list)[(tmp > r[:,None]).argmax(axis=1)] # array(['col3', 'col3', 'col1', 'col2'], dtype='<U4')
Question: I'm sorry if the title is confusing since English is not my first language. What I have trouble is running a python script that calls import of its own directory. The folder structure is like this: MyProject |--Utils |-- util | |-- __init__.py | |-- run.py |-- __init__.py |-- test.py  And the code of test.py is as following import Utils.util if __name__ == '__main__': # Do something  When running test.py, I get this error ModuleNotFoundError: No module named 'Utils'  Is there anyway to call import that's suitable for this folder structure? If it's possible, someone can please help me naming this problem for better searches? I have tried create a new script in the root folder which runs normally. However, what if I want to store testing code like test.py in modular folders, the import command is not running

Answer: The error "ModuleNotFoundError: No module named 'Utils'" occurs because Python doesn't automatically search for modules within subdirectories unless they are treated as packages. from .util import