简体   繁体   中英

Finding all rectangles in Python list

I have a python list:

[
[1, 1, 2],
[1, 1, 2],
[7, 4, 5],
[5, 3, 7],
]

I need to find all rectangles (how many rectangles are there) in this 2D array that meet this criteria:
1. All numbers in that one rectangle must be the same
2. Number from that rectangle can't be anywhere else in the array
3. Not really a criteria but some extra info:
Rectangles dimensions can be 1x1, 2x1, 2x2, 3x1, 3x2, 3x3, 3x4 and so on.

In this example there are 5 rectangles for numbers:

1, 2, 3, 4

and numbers 3, 1, 8 don't meet the criteria because:

5 - Breaks rule number 2
7 - Breaks rule number 2

I tried stuff like finding nearest elements that comparing them and if they match go down one row and so on but I couldn't do it so I hope someone could help. Thanks.

One approach is for each unique value, fit a rectangle over all instances of that value. If all values within that rectangle match, then you've met your criteria. Here it is implemented in code using numpy, printing values that match the criteria:

import numpy as np

arr = np.array([
    [3, 2, 2, 1, 4],
    [3, 2, 2, 7, 4],
    [8, 2, 2, 1, 3],
    [8, 8, 9, 9, 9],
    [8, 8, 1, 5, 1]
])
for n in np.unique(arr):
    y, x = np.where(arr == n)
    if (arr[y.min():y.max()+1, x.min():x.max()+1] == n).all():
        print(n)

Update

Not as pretty but this something like this would not require numpy:

lst = [
    [3, 2, 2, 1, 4],
    [3, 2, 2, 7, 4],
    [8, 2, 2, 1, 3],
    [8, 8, 9, 9, 9],
    [8, 8, 1, 5, 1]
]

for val in set([x for sub in lst for x in sub ]):
    y = [n for n, sub in enumerate(lst) if val in sub]
    if any(y):
        x = []
        for sub in [lst[n] for n in y]:
            x.extend([m for m, v in enumerate(sub) if v == val])

        rect = [i for sub in lst[min(y):max(y)+1] for i in sub[min(x):max(x)+1]]
        if all([i == val for i in rect]):
            print(val)

Further to the comment above from user3386109, here is the pure python code that will calculate the rectangle size from the top left coordinate to the bottom right coordinate and compare that to the total digits and see if they equal. If they do, then the digit is a rectangle.

# create an empty result array
result = []

# check each digit one through nine
for num in range(1, 10):
    # create a list to store the coordinates of each found digit
    coords = []
    # rows loop
    for r in range(len(li)):
        # columns loop
        for c in range(len(li[0])):
            if li[r][c] == num:
                coords.append([r,c])
    # check for null coords list which means the digit is not in the matrix
    if len(coords) == 0:
        pass
    else:
        tl = coords[0]
        br = coords[-1]
        total = (br[0] - tl[0] + 1) * (br[1] - tl[1] + 1)
        if total == len(coords):
            result.append(num)
print('The digits that form rectangles are: ', result)

The result will look like this:

The digits that form rectangles are:  [2, 4, 5, 7, 9]

Another approach is to consider using a pandas dataframe.

import pandas as pd

Make a dataframe from your list of numbers as follows:

df = pd.DataFrame([
[3, 2, 2, 1, 4],
[3, 2, 2, 7, 4],
[8, 2, 2, 1, 3],
[8, 8, 9, 9, 9],
[8, 8, 1, 5, 1]
])

Then you use a for loop to test each number 1-9 (I'm assuming 0 is not in your list?) and in the for loop you drop the rows and columns that don't have any of the number being tested. If the resulting frame is indeed a rectangle, then there should be no 'NaN's present.

For example, the digit 1 resulting frame looks like:

      2   3   4
0    NaN 1.0 NaN
2    NaN 1.0 NaN
4    1.0 NaN 1.0

Whereas the digit 2 resulting frame looks like:

     1   2
0   2.0 2.0
1   2.0 2.0
2   2.0 2.0

Check for NaN's being == 0 and you will have a rectangle. You also need to make sure the size of the field is not zero, as this would indicate a number not present. Here's the code:

result = []
for num in range(1, 10):
    df_dna = df[df == num].dropna(how="all", axis=0).dropna(how="all", axis=1)
    if df_dna.isnull().sum().sum() == 0 and df_dna.size != 0:
        result.append(num)
print("The numbers that form rectangles are:", result)

And your result looks like this:

The numbers that form rectangles are: [2, 4, 5, 7, 9]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM