eshangj/stackoverflow_q_and_a_sample huggingface dataset:
Question: This question is related to Calling a static method with self vs. class name but I'm trying to understand the behavior when you wrap a static method so I can fix my wrapper. For example: import functools def wrap(f): @functools.wraps(f) def wrapped(*args, **kwargs): print(f"{f.__name__} called with args: {args}, kwargs: {kwargs}") return f(*args, **kwargs) return wrapped class Test: @staticmethod def static(): print("hello") def method(self): self.static() Test.static = wrap(Test.static) Test().method()  will produce: static called with args: (<__main__.Test object at 0x1050b3fd0>,), kwargs: {} Traceback (most recent call last): File "/Users/aiguofer/decorator_example.py", line 20, in <module> Test().meth() File "/Users/aiguofer/decorator_example.py", line 16, in meth self.static() File "/Users/aiguofer/decorator_example.py", line 7, in wrapped return f(*args, **kwargs) TypeError: Test.static() takes 0 positional arguments but 1 was given  However, if I change self.static() -> Test.static(), we get the expected output: static called with args: (), kwargs: {} hello  My use case is that I need to wrap some methods from an external library, including a staticmethod on a class. Within that class, they call the static method from an instance method using self.<method_name>, which is causing the above issue in my wrapper. I thought I might be able to deal with this issue with a isinstance(f, staticmethod) but that seems to return False. I'd love to understand what is happening as well as potential solutions to this problem!

Answer: Method access in Python works by using the descriptor protocol to customize attribute access. When you access a staticmethod, it uses the descriptor protocol to make the attribute access return the underlying function. That's why isinstance(f, staticmethod) reported False, in the versions of your code where you tried that. Then when you try to assign Test.static = wrap(Test.static), wrap returns an ordinary function object. When you access one of those on an instance, they use the descriptor protocol to return a method object, with the first argument bound to the instance. You need to create a staticmethod object, to get staticmethod descriptor handling. You can bypass the descriptor protocol with inspect.getattr_static: import inspect import types def wrap_thing(thing): if isinstance(thing, types.FunctionType): return wrap(thing) elif isinstance(thing, staticmethod): return staticmethod(wrap(thing.__func__)) elif isinstance(thing, classmethod): return classmethod(wrap(thing.__func__)) elif isinstance(thing, property): fget, fset, fdel = [None if attr is None else wrap(attr) for attr in [thing.fget, thing.fset, thing.fdel]] return property(fget, fset, fdel, thing.__doc__) else: raise TypeError(f'unhandled type: {type(thing)}') Test.static = wrap_thing(inspect.getattr_static(Test, 'static'))
Question: I'm looking for a function capable of taking a m x n array, which repeats each row n times over a identity-like grid of m size. For demo: input = [[a1, b1, c1], [a2, b2, c2]] output = [[a1, b1, c1, 0, 0, 0], [a1, b1, c1, 0, 0, 0], [a1, b1, c1, 0, 0, 0], [ 0, 0, 0, a2, b2, c2], [ 0, 0, 0, a2, b2, c2], [ 0, 0, 0, a2, b2, c2]]  Last time I asked something similar I was told of the Kronecker product, is there some similar function?

Answer: While looking similar, it appears to me that the given problem cannot be solved using a Kronecker product: with the latter, you could only manage to get repetitions of your complete input matrix as blocks of the result matrix. I stand corrected: For a solution that employs the Kronecker product, see @ThomasIsCoding's answer. In any case, what you need is the individual rows, repeated, as blocks of the result matrix. So that is what the code below does: it constructs a block matrix from the rows, using scipy.linalg.block_diag(), then repeats them as required, using numpy.repeat(). Note that block_diag() expects individual blocks as individual arguments, which is why a is unpacked with the * prefix. import numpy as np import scipy a = np.asarray([[11, 21, 31], [21, 22, 32], [31, 32, 33]]) print(a) # [[11 21 31] # [21 22 32] # [31 32 33]] blocked = scipy.linalg.block_diag(*a) print(blocked) # [[11 21 31 0 0 0 0 0 0] # [ 0 0 0 21 22 32 0 0 0] # [ 0 0 0 0 0 0 31 32 33]] result = np.repeat(blocked, a.shape[1], axis=0) print(result) # [[11 21 31 0 0 0 0 0 0] # [11 21 31 0 0 0 0 0 0] # [11 21 31 0 0 0 0 0 0] # [ 0 0 0 21 22 32 0 0 0] # [ 0 0 0 21 22 32 0 0 0] # [ 0 0 0 21 22 32 0 0 0] # [ 0 0 0 0 0 0 31 32 33] # [ 0 0 0 0 0 0 31 32 33] # [ 0 0 0 0 0 0 31 32 33]]
Question: I find myself doing this: import polars as pl import sys red_data = pl.DataFrame( [ pl.Series("id", [0, 1, 2], dtype=pl.UInt8()), pl.Series("red_data", [1, 0, 1], dtype=pl.UInt8()), ] ) blue_data = pl.DataFrame( [ pl.Series("id", [0, 2, 3], dtype=pl.UInt8()), pl.Series("blue_data", [0, 1, 1], dtype=pl.UInt8()), ] ) # in both red and blue red_and_blue = red_data.join(blue_data, on=["id"]) # in red, but not blue red_not_blue = red_data.join(blue_data, on=["id"], how="anti").with_columns( blue_data=pl.lit(None, dtype=pl.UInt8()) ) # in blue, but not red blue_not_red = blue_data.join(red_data, on=["id"], how="anti").with_columns( red_data=pl.lit(None, dtype=pl.UInt8()) ) columns = ["id", "red_data", "blue_data"] sys.displayhook( pl.concat( [ red_and_blue.select(columns), red_not_blue.select(columns), blue_not_red.select(columns), ] ) )  shape: (4, 3) ┌─────┬──────────┬───────────┐ │ id ┆ red_data ┆ blue_data │ │ --- ┆ --- ┆ --- │ │ u8 ┆ u8 ┆ u8 │ ╞═════╪══════════╪═══════════╡ │ 0 ┆ 1 ┆ 0 │ │ 2 ┆ 1 ┆ 1 │ │ 1 ┆ 0 ┆ null │ │ 3 ┆ null ┆ 1 │ └─────┴──────────┴───────────┘ 

Answer: It seems like you are looking for a simple pl.DataFrame.join with how="full" and coalesce=True. red_data.join(blue_data, on="id", how="full", coalesce=True)  shape: (4, 3) ┌─────┬──────────┬───────────┐ │ id ┆ red_data ┆ blue_data │ │ --- ┆ --- ┆ --- │ │ u8 ┆ u8 ┆ u8 │ ╞═════╪══════════╪═══════════╡ │ 0 ┆ 1 ┆ 0 │ │ 1 ┆ 0 ┆ null │ │ 2 ┆ 1 ┆ 1 │ │ 3 ┆ null ┆ 1 │ └─────┴──────────┴───────────┘
Question: I'm trying to debug (rewrite?) someone else's Python/cherrypy web app, and I ran across the following 'if' statement: if not filename.endswith(".dat") and ( filename.endswith(".dat") or not filename.endswith(".cup") ): raise RuntimeError( "Waypoint file {} has an unsupported format.".format( waypoint_file.filename ) )  I think this is the same as: if not A and (A or not B):  If so, then:  if A = False, then it reduces to if True and (False or not B):  if True and not B = not B   if A = True, then it reduces to if False: i.e. the if block will never execute   I'm pretty sure that the intent of the if block is to warn the user that the extension of the file in question is neither .DAT nor .CUP, but it doesn't look to me that it actually executes that intent. I think the if block should be: if(not .DAT and not .CUP) = if not(.DAT or .CUP)  Is that correct?

Answer: As you have two variables which could only have one of two values you can easily test each case, for example by doing for a in [False, True]: for b in [False, True]: print(a, b) if not a and (a or not b): print("Condition hold") else: print("Condition does not hold")  which gives output False False Condition hold False True Condition does not hold True False Condition does not hold True True Condition does not hold  As you can see it does only hold when both are False, therefore it is equivalent to if not a and not b:  However in this particular case you do not even needs 2 conditions, as endswith argument  can also be a tuple of suffixes to look for  therefore you can write if not filename.endswith((".dat", ".cup")): print("Unsupported format")
Question: I am recently learning some HPC topics and get to know that modern C/C++ compilers is able to detect places where optimization is entitled and conduct it using corresponding techniques such as SIMD, loop unfolding, etc, especially under flag -O3, with a tradeoff between runtime performance vs compile time and object file size. Then it immediately occurred to me that CPython interprets and executes on-fly, so I assume it cannot afford to conduct those compiler features because compiling time for it is equivalently runtime, so I did a toy experiment below: import time, random n = 512 A = [[random.random() for _ in range(n)] for _ in range(n)] B = [[random.random() for _ in range(n)] for _ in range(n)] C = [[0] * n for _ in range(n)] def matMul( A, B, C ): """ C = A .* B """ for i in range(0, n - 4, 4): for j in range(0, n - 4, 4): for k in range(0, n - 4, 4): C[i][j] = A[i][k] * B[k][j] C[i + 1][j + 1] = A[i + 1][k + 1] * B[k + 1][j + 1] C[i + 2][j + 2] = A[i + 2][k + 2] * B[k + 2][j + 2] C[i + 3][j + 3] = A[i + 3][k + 3] * B[k + 3][j + 3] C[i + 4][j + 4] = A[i + 4][k + 4] * B[k + 4][j + 4] # return C start = time.time() matMul( A, B, C ) end = time.time() print( f"Elapsed {end - start}" )  With the loop unfolding, the program finishes within 3 seconds, without it, it takes up to almost 20 seconds. Does that mean one needs to pay attention and manually implement those opt techniques when writing Python code? or does Python offer the optimization under any special setting?

Answer: Loop unrolling is useful because it can (1) reduce the overhead spent managing the loop and (2) at the assembly level, it let the processor run faster by eliminating branch penalties, keeping the instruction pipeline full, etc. (2) doesn't really apply to an interpreted language implementation like Python - it's already doing lots of branching and decision-making at the assembly level. It might gain you with (1), but my gut feeling is that that time is often dwarfed by interpreter overhead. The golden rule of performance optimization is to first measure and confirm that the bottleneck is where you think it is. Incidentally, your code has a bug:  C[i][j] = A[i][k] + B[k][j] C[i + 1][j + 1] = A[i + 1][k + 1] + B[k + 1][j + 1] C[i + 2][j + 2] = A[i + 2][k + 2] + B[k + 2][j + 2] C[i + 3][j + 3] = A[i + 3][k + 3] + B[k + 3][j + 3] C[i + 4][j + 4] = A[i + 4][k + 4] + B[k + 4][j + 4]  It processes cells (0, 0), (1, 1), (2, 2), (3, 3), and (4, 4) (even though (4, 4) will also be processed on the next iteration), but not (0, 1), (0, 2), (1, 0)... (That's the other reason for the golden rule of performance optimization: it's easy to make mistakes by optimizing code that doesn't need it.) As @Mat said, the standard approach for Python in particular is to use NumPy, which uses an optimized C implementation. All the above applies to CPython, the standard Python implementation. There are other Python implementations like Cython that offer their own optimizations; I'm less familiar with those.
Question: Given the following dataframe: Year 2024 2023 2022 Header N Result SD N Result SD N Result SD Vendor A 5 20 3 5 22 4 1 21 3 B 4 25 2 4 25 3 4 26 5 C 9 22 3 9 27 1 3 23 3 D 3 23 5 3 16 2 5 13 4 E 5 27 2 5 21 3 3 19 5  I would like to calculate for each year the mean value of the results column and then create a column, where the relative deviation to the mean is displayed (e.g. Results Value / mean-value * 100). The N and SD column were just included for completeness and is not needed for the calculation. Year 2024 2023 2022 Header N Result SD Deviation N Result SD Deviation N Result SD Deviation Vendor A 5 20 3 85.5 5 22 4 99.1 1 21 3 .. B 4 25 2 106 4 25 3 113 4 26 5 .. C 9 22 3 .. 9 27 1 .. 3 23 3 .. D 3 23 5 .. 3 16 2 .. 5 13 4 .. E 5 27 2 .. 5 21 3 .. 3 19 5 ..  How what i be able to achieve that? Thanks a lot in advance!

Answer: Use DataFrame.xs for select Result labels in MultiIndex, divide by mean and append to original in concat, last for correct position add DataFrame.sort_index with parameter sort_remaining=False: df1 = df.xs('Result', axis=1, level=1, drop_level=False) out = (pd.concat([df, df1.div(df1.mean()).mul(100) .rename(columns={'Result':'Deviation'})], axis=1) .sort_index(axis=1, ascending=False, level=0, sort_remaining=False)) print (out) 2024 2023 2022 \ N Result SD Deviation N Result SD Deviation N Result SD A 5 20 3 85.470085 5 22 4 99.099099 1 21 3 B 4 25 2 106.837607 4 25 3 112.612613 4 26 5 C 9 22 3 94.017094 9 27 1 121.621622 3 23 3 D 3 23 5 98.290598 3 16 2 72.072072 5 13 4 E 5 27 2 115.384615 5 21 3 94.594595 3 19 5 Deviation A 102.941176 B 127.450980 C 112.745098 D 63.725490 E 93.137255  Another loop idea: for x in df.columns.levels[0]: df[(x, 'Deviation')] = df[(x, 'Result')].div(df[(x, 'Result')].mean()).mul(100) out = df.sort_index(axis=1, ascending=False, level=0, sort_remaining=False) print (out) 2024 2023 2022 \ N Result SD Deviation N Result SD Deviation N Result SD A 5 20 3 85.470085 5 22 4 99.099099 1 21 3 B 4 25 2 106.837607 4 25 3 112.612613 4 26 5 C 9 22 3 94.017094 9 27 1 121.621622 3 23 3 D 3 23 5 98.290598 3 16 2 72.072072 5 13 4 E 5 27 2 115.384615 5 21 3 94.594595 3 19 5 Deviation A 102.941176 B 127.450980 C 112.745098 D 63.725490 E 93.137255
Question: I am using PIL in python to extract the metadata of an image. Here is my code: import json from PIL import Image, TiffImagePlugin import PIL.ExifTags img = Image.open("/home/user/DSCN0010.jpg") dct = { PIL.ExifTags.TAGS[k]: float(v) if isinstance(v, TiffImagePlugin.IFDRational) else v for k, v in img._getexif().items() if k in PIL.ExifTags.TAGS } print(json.dumps(dct))  I'm getting the following error: Error processing EXIF data: Object of type IFDRational is not JSON serializable  As you can see in the code, I cast all the values of type IFDRational to float but I'm still getting the error. Here is the link to the image: https://github.com/ianare/exif-samples/blob/master/jpg/gps/DSCN0010.jpg

Answer: The problem is that you are just casting to float the IFDRational values that are directly in the root of the EXIF items. However, it looks like one of those items, called GPSInfo, is a dict that contains internally more IFDRational values. You would need a function to sanitise the values, which would ideally iterate recursively all possible nested data so that conversion is done at all levels. An initial idea would be to do it like this: import json import PIL.ExifTags from PIL import Image, TiffImagePlugin img = Image.open("/home/user/DSCN0010.jpg") def sanitise_value(value): # Base case: IFDRational to float if isinstance(value, TiffImagePlugin.IFDRational): return float(value) # Dict case: sanitise all values if isinstance(value, dict): for k, v in value.items(): value[k] = sanitise_value(v) # List/tuple case: sanitise all values and convert to list, # as a tuple in JSON will anyway be a list elif isinstance(value, (list, tuple)): value = [sanitise_value(i) for i in value] # Extra case: some values are byte-strings, so you have to # decode them in order to make them JSON serializable. I # decided to use 'replace' in case some bytes cannot be # decoded, but there are other options elif isinstance(value, (bytes, bytearray)): value = value.decode("utf-8", "replace") return value dct = { PIL.ExifTags.TAGS[k]: sanitise_value(v) # <-- We use here the sanitising function for k, v in img._getexif().items() if k in PIL.ExifTags.TAGS } print(json.dumps(dct))  This would work with that image, but feel free to test with other scenarios just in case it's still not a universal solution. Plus, keep in mind the comment regarding the byte-strings, because you might need to decode them in a different way depending on your needs. For instance, you might prefer to decode it as latin-1 instead of utf-8, or use other type of error handling as stated here. Hope this helps!
Question: I have a 2D array of (4,5) and another 2D array of (4,2) shape. The second array contains the start and end indices that I need to filter out from first array i.e., I want to slice the first array using second array. np.random.seed(0) a = np.random.randint(0,999,(4,5)) a array([[684, 559, 629, 192, 835], [763, 707, 359, 9, 723], [277, 754, 804, 599, 70], [472, 600, 396, 314, 705]])  idx = np.array([[2,4], [0,3], [2,3], [1,3] ])  Expected output - can be either of following two formats. Only reason for padding with zeros is that variable length 2d arrays are not supported. [[629, 192, 835, 0, 0], [763, 707, 359, 9, 0], [804, 599, 0, 0, 0], [600, 396, 314, 0, 0] ]  [[0, 0, 629, 192, 835], [763, 707, 359, 9, 0], [0, 0, 804, 599, 0], [0, 600, 396, 314, 0] ] 

Answer: Another possible solution, which uses:  np.arange to create a range of column indices based on the number of columns in a.  A boolean mask m is created using logical operations to check if each column index falls within the range specified by idx. The np.newaxis is used to align dimensions for broadcasting.  np.where is used to create a_mask, where elements in a are replaced with 0 if the corresponding value in m is False.  np.argsort is used to get the indices that would sort each row of m (negated) in ascending order.  np.take_along_axis is used to rearrange the elements of a_mask based on the sorted indices.   cols = np.arange(a.shape[1]) m = (cols >= idx[:, 0, np.newaxis]) & (cols <= idx[:, 1, np.newaxis]) a_mask = np.where(m, a, 0) sort_idx = np.argsort(~m, axis=1) np.take_along_axis(a_mask, sort_idx, axis=1)  NB: Notice that a_mask contains the unsorted version of the solution (that is essentially the approach followed by @mozway).  Output: array([[629, 192, 835, 0, 0], [763, 707, 359, 9, 0], [804, 599, 0, 0, 0], [600, 396, 314, 0, 0]]) # a_mask array([[ 0, 0, 629, 192, 835], [763, 707, 359, 9, 0], [ 0, 0, 804, 599, 0], [ 0, 600, 396, 314, 0]])
Question: import pandas as pd import numpy as np # Sample data index = pd.MultiIndex.from_tuples([ ('A', 'a1', 'x'), ('A', 'a1', 'y'), ('A', 'a2', 'x'), ('A', 'a2', 'y'), ('B', 'b1', 'x'), ('B', 'b1', 'y'), ('B', 'b2', 'x'), ('B', 'b2', 'y') ], names=['level_1', 'level_2', 'level_3']) data = np.random.randn(len(index)) df = pd.DataFrame(data, index=index, columns=['value'])  Say I have for example the above dataframe, which is multi-indexed with 3 levels. Now, my goal is to select a subset from this dataframe, where the first two levels of index comes from a subset of the cartesian product (A,B) * (a1, a2, b1, b2), say S = [(A, a1), (B, b2)]. I want to keep third level of the multi-index. I expect the result to be like Original DataFrame: value level_1 level_2 level_3 A a1 x 0.123456 y 0.234567 a2 x 0.345678 y 0.456789 B b1 x 0.567890 y 0.678901 b2 x 0.789012 y 0.890123 Subset DataFrame: value level_1 level_2 level_3 A a1 x 0.123456 y 0.234567 B b2 x 0.789012 y 0.890123 

Answer: Use DataFrame.droplevel for remove 3 level, so possible filter by subset by Index.isin in boolean indexing: S = [('A', 'a1'), ('B', 'b2')] out = df[df.droplevel(2).index.isin(S)] print (out) value level_1 level_2 level_3 A a1 x 0.545790 y -1.298511 B b2 x 0.018436 y -1.076408
Question: i have data structured like this (working with pandas): ID|comp_1_name|comp_1_percentage|comp_2_name|comp_2_percentage| 1| name_1 | 13 | name_2 | 33 | 2| name_3 | 15 | name_1 | 46 |  There are six comp_name/comp_percentage couples. Names are not equally distributed in all six "*_name" columns. I would like to obtain that kind of trasformation: ID|name_1|name_2|name_3| 1| 13 | 33 | 0 | 2| 46 | 0 | 15 |  I tried transposing (.T) both the entire dataframe and isolating the comp_name, comp_percentage couples, but to no avail.

Answer: You can try using pd.wide_to_long with a little column renaming and shaping dataframe: # Renaming columns to move name and percentage to the front for pd.wide_to_long dfr = df.rename(columns=lambda x: '_'.join(x.rsplit('_', 1)[::-1])) (pd.wide_to_long(dfr, ['name_', 'percentage_'], 'ID', 'No', suffix='.*') .reset_index() .pivot(index='ID', columns='name_', values='percentage_') .fillna(0) .reset_index() .rename_axis(None, axis=1) .astype(int))  Output:  ID name_1 name_2 name_3 0 1 13 33 0 1 2 46 0 15
Question: all Python experts, I'm a Python newbie, stuck with a problem which may look very simple to you. Say I have a data frame of 100 rows, how can I split it into 5 sub-frames, each of which contains the rows of 5n+0, 5n+1, 5n+2, 5n+3 and 5n+4 respectively? For instance, the 0th, 5th, 10th up to the 95th will go to one sub-frame, 1st, 6th, 11th up to 96th will go to the 2nd sub-frame and the 4th, 9th, 14th up to 99th will go to the 5th sub-frame? This is what I have tried: grouped = l_df.groupby(l_df.index//5) a_df = grouped.get_group(1) b_df = grouped.get_group(2) c_df = grouped.get_group(3) d_df = grouped.get_group(4)  But each of my group got only 5 rows. Any suggestions? Thanks much in advance!

Answer: Just use iloc and slice as you would do with a list i.e. start:end:step. Example: df = pd.DataFrame({"A":range(100)}) display(df.T) display(df.iloc[0::5].T) display(df.iloc[1::5].T) display(df.iloc[2::5].T) # ...
Question: I'm trying to find more decimal places to the 'Prime Constant'. The output maxes out at 51 decimal places after using getcontext().prec=100 from decimal import * getcontext().prec = 100 base = 2 s = "0.0110101000101000101000100000101000001000101000100000100000101000001000101000001000100000100000001000101000101000100000000000001000100000101000000000101000001000001000100000100000101000000000101000101000000000001000000000001000101000100000101000000000100000100000100000101000001000101000000000100000000000001000101000100000000000001000001000000000101000100000100000001000001000001000100000100000001000100000001000000000101000000000101000001000100000100000001000101000100000000000100000001000100000001000100000100000000000101000000000000000001000001000000000100000100000101000001000000000100000100000101000001000001000101000000000001000000000101000100000100000101000000000001000100000100000001000000000100000001000000000100000001000001000001000100000001000001000100000001000100000000000001000000000100000000000101000000000101000101000000000100000000000001000101000100000000000001000101000100000000000000000001000100000001000000000100000001000100000100000100000000000001000100000100000100000001000001000000000001000100000101000" split = s.split(".") joinsplit = "".join(split) position = len(split[1]) if len(split) > 1 else 0 divisor = base ** position base10 = Decimal(int(joinsplit, base=base) / divisor) print(base10) 

Answer: Try base10 = Decimal(int(joinsplit, base=base)) / Decimal(divisor)  You need to have the Decimal package doing the division.
Question: I stumbled upon this code while debugging something. Is the second line a valid Python code? I tried running this and it ran successfully without any errors. Shouldn't I be getting a syntax error since = is used for assigning values in dictionaries? dict1 = {'temp':10} dict1['temp'] : 5 

Answer: What you're seeing is the type annotation syntax. In your case it does absolutely nothing but is not invalid.  Annotations must be valid expressions that evaluate without raising exceptions at the time the function is defined (but see below for forward references).
Question: I am using the tensorflow tdfs dataset extreme/pos which I retrieve using the code below. It is annotated with universal part of speech POS labels. These are int values. Its fairly easy to map them back to their part of speech by creating my own mapping (0 = ADJ, 7 = NOUN, etc.) but I was wondering if there is a way of retrieving these class mappings from the tdfs dataset? (orig_train, orig_dev, orig_test), ds_info = tfds.load( 'xtreme_pos/xtreme_pos_en', split=['train', 'dev', 'test'], shuffle_files=True, with_info=True ) 

Answer: One way is to dig into Tensorflow code to see where is defined the list of POS and then import it to use in your code. You can find the list of the POS in the Github code of tensorflow Datasets there (UPOS constant): https://github.com/tensorflow/datasets/blob/master/tensorflow_datasets/core/dataset_builders/conll/conllu_dataset_builder_utils.py#L31 The item order is their index so with display(pd.Series(UPOS)), you get:   Another way would be to extract the items from the upos column of tfds.as_dataframe (taking a few rows, concatenating the upos values, splitting by the separating character and taking the set() to get the unique values.