Home > Software design >  How to group elements in dataframe by row
How to group elements in dataframe by row

Time:01-01

I have csv example

Here's this same dataframe in dict form for testing

{0: {0: 'minecraft:diorite', 1: 'minecraft:diorite', 2: 'minecraft:stone'},
 1: {0: 'minecraft:stone', 1: 'minecraft:stone', 2: 'minecraft:cobblestone'},
 2: {0: 'minecraft:block_of_raw_iron',
  1: 'minecraft:diamond_block',
  2: 'minecraft:gold_block'}}

I want my txt output to be

/fill 0 0 0 0 0 1 minecraft:diorite
/fill 0 0 2 0 0 2 minecraft:stone

/fill 1 0 0 1 0 1 minecraft:stone
/fill 1 0 2 1 0 2 minecraft:cobblestone

/fill 2 0 0 2 0 0 minecraft:block_of_raw_iron
/fill 2 0 1 2 0 1 minecraft:diamond_block
/fill 2 0 2 2 0 2 minecraft:gold_block

To those who don't know minecraft command syntax:

/fill {x_start} {y_start} {z_start} {x_end} {y_end} {z_end} {block}

with [start, end] (both included) and x going sideways and z going up or down.

I've tried nearly everything but I almost gave up on this code and so I deleted my progress, sorry :(

Can anyone help? Thanks in advance.

Edit 1.: For even better testing, here's Naruto's pixel art (the first 5 rows)

{0: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 1: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 2: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 3: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 4: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 5: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 6: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 7: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 8: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 9: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 10: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 11: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 12: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 13: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 14: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:block_of_raw_iron'}, 15: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:sand'}, 16: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:snow_block', 4: 'minecraft:snow_block'}, 17: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:snow_block', 4: 'minecraft:snow_block'}, 18: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:snow_block', 4: 'minecraft:snow_block'}, 19: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:sand', 4: 'minecraft:sand'}, 20: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:sand', 3: 'minecraft:block_of_gold', 4: 'minecraft:sponge'}, 21: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:snow_block', 3: 'minecraft:diorite_slab', 4: 'minecraft:sand'}, 22: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:snow_block', 3: 'minecraft:snow_block', 4: 'minecraft:snow_block'}, 23: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:snow_block', 3: 'minecraft:snow_block', 4: 'minecraft:diorite_slab'}, 24: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:snow_block', 3: 'minecraft:sand', 4: 'minecraft:block_of_gold'}, 25: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:sand', 3: 'minecraft:sponge', 4: 'minecraft:sponge'}, 26: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:sand', 4: 'minecraft:white_terracotta'}, 27: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:snow_block', 4: 'minecraft:snow_block'}, 28: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:snow_block', 4: 'minecraft:diorite_slab'}, 29: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:snow_block', 4: 'minecraft:sand'}, 30: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:sand', 4: 'minecraft:sponge'}, 31: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 32: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:snow_block'}, 33: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 34: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 35: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 36: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 37: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 38: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 39: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 40: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 41: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 42: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 43: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 44: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}, 45: {0: 'minecraft:diorite_slab', 1: 'minecraft:diorite_slab', 2: 'minecraft:diorite_slab', 3: 'minecraft:diorite_slab', 4: 'minecraft:diorite_slab'}}

And (what I think it is) the expected output for the first 3 columns (again, assuming player's position 0, 0, 0).

/fill 0 0 0 0 0 45 minecraft:diorite_slab
/fill 1 0 0 1 0 45 minecraft:diorite_slab

/fill 2 0 0 2 0 26 minecraft:diorite_slab
/fill 2 0 27 2 0 29 minecraft:mushroom_stem
/fill 2 0 30 2 0 45 minecraft:diorite_slab

CodePudding user response:

I tried writing a solution to this based on un-pivoting the matrix of values into a coordinate representation, but the compression code ended up being hideously complicated, so I scrapped that, and wrote a solution based on a nested loop instead, which ended up being much easier to write.

Basically, this loops over rows, and within each row, it groups blocks of the same material, as long as those blocks are contiguous. (That's the purpose of itertools.groupby(), which is different from the Pandas groupby because the Pandas version will group non-contiguous values which are the same.)

import pandas as pd
import itertools
from collections import namedtuple

FillCommand = namedtuple('FillCommand', 'minx minz maxx maxz blocks material')

def compress_row(j, s):
    i = 0
    for material, group in itertools.groupby(s):
        num_elements = len(list(group))
        maxz = i   num_elements - 1
        yield FillCommand(
            minx=j,
            minz=i,
            maxx=j,
            maxz=maxz,
            blocks=num_elements,
            material=material,
        )
        i  = num_elements

def compress_all(df):
    height = len(df.index)
    for j in range(height):
        yield from compress_row(j, df.iloc[j])

def fill_to_string(fill_commands):
    for cmd in fill_commands:
        yield f"/fill {cmd.minx} 0 {cmd.minz} {cmd.maxx} 0 {cmd.maxz} {cmd.material}"

def convert_pic_to_fill(df):
    return list(fill_to_string(compress_all(df)))

convert_pic_to_fill(df_naruto)

Here's the output of this code:

/fill 0 0 0 0 0 45 minecraft:diorite_slab
/fill 1 0 0 1 0 45 minecraft:diorite_slab
/fill 2 0 0 2 0 19 minecraft:diorite_slab
/fill 2 0 20 2 0 20 minecraft:sand
/fill 2 0 21 2 0 24 minecraft:snow_block
/fill 2 0 25 2 0 25 minecraft:sand
/fill 2 0 26 2 0 45 minecraft:diorite_slab
/fill 3 0 0 3 0 15 minecraft:diorite_slab
/fill 3 0 16 3 0 18 minecraft:snow_block
/fill 3 0 19 3 0 19 minecraft:sand
/fill 3 0 20 3 0 20 minecraft:block_of_gold
/fill 3 0 21 3 0 21 minecraft:diorite_slab
/fill 3 0 22 3 0 23 minecraft:snow_block
/fill 3 0 24 3 0 24 minecraft:sand
/fill 3 0 25 3 0 25 minecraft:sponge
/fill 3 0 26 3 0 26 minecraft:sand
/fill 3 0 27 3 0 29 minecraft:snow_block
/fill 3 0 30 3 0 30 minecraft:sand
/fill 3 0 31 3 0 45 minecraft:diorite_slab
/fill 4 0 0 4 0 13 minecraft:diorite_slab
/fill 4 0 14 4 0 14 minecraft:block_of_raw_iron
/fill 4 0 15 4 0 15 minecraft:sand
/fill 4 0 16 4 0 18 minecraft:snow_block
/fill 4 0 19 4 0 19 minecraft:sand
/fill 4 0 20 4 0 20 minecraft:sponge
/fill 4 0 21 4 0 21 minecraft:sand
/fill 4 0 22 4 0 22 minecraft:snow_block
/fill 4 0 23 4 0 23 minecraft:diorite_slab
/fill 4 0 24 4 0 24 minecraft:block_of_gold
/fill 4 0 25 4 0 25 minecraft:sponge
/fill 4 0 26 4 0 26 minecraft:white_terracotta
/fill 4 0 27 4 0 27 minecraft:snow_block
/fill 4 0 28 4 0 28 minecraft:diorite_slab
/fill 4 0 29 4 0 29 minecraft:sand
/fill 4 0 30 4 0 30 minecraft:sponge
/fill 4 0 31 4 0 31 minecraft:diorite_slab
/fill 4 0 32 4 0 32 minecraft:snow_block
/fill 4 0 33 4 0 45 minecraft:diorite_slab

CodePudding user response:

Here is a solution using mostly shift and groupby:

def script(df):
    z = (df != df.shift()).cumsum()
    zri = z.reset_index()
    ix_name = z.index.name
    co_name = z.columns.name
    for i in z:
        v = zri.groupby(i)[ix_name].agg(['first', 'last'])
        s = {co_name:i}
        e = {co_name:i}
        for _, r in v.iterrows():
            s[ix_name] = r['first']
            e[ix_name] = r['last']
            material = df.loc[r['first'], i]
            yield f'/fill {s["x"]} 0 {s["z"]} {e["x"]} 0 {e["z"]} {material}'

How to use:

# df is the DataFrame of your dict

# label the axes
df = df.rename_axis(index='z', columns='x')

# select which axis to make major
a = list(script(df))
b = list(script(df.T))
res = min([a, b], key=len)

On your first sample data:

>>> res
['/fill 0 0 0 0 0 1 minecraft:diorite',
 '/fill 0 0 2 0 0 2 minecraft:stone',
 '/fill 1 0 0 1 0 1 minecraft:stone',
 '/fill 1 0 2 1 0 2 minecraft:cobblestone',
 '/fill 2 0 0 2 0 0 minecraft:block_of_raw_iron',
 '/fill 2 0 1 2 0 1 minecraft:diamond_block',
 '/fill 2 0 2 2 0 2 minecraft:gold_block']

On the first 5 rows you provided for "Naruto's pixel art":

>>> res
['/fill 0 0 0 45 0 0 minecraft:diorite_slab',
 '/fill 0 0 1 45 0 1 minecraft:diorite_slab',
 '/fill 0 0 2 19 0 2 minecraft:diorite_slab',
 '/fill 20 0 2 20 0 2 minecraft:sand',
 '/fill 21 0 2 24 0 2 minecraft:snow_block',
 '/fill 25 0 2 25 0 2 minecraft:sand',
 '/fill 26 0 2 45 0 2 minecraft:diorite_slab',
 '/fill 0 0 3 15 0 3 minecraft:diorite_slab',
 '/fill 16 0 3 18 0 3 minecraft:snow_block',
 '/fill 19 0 3 19 0 3 minecraft:sand',
 '/fill 20 0 3 20 0 3 minecraft:block_of_gold',
 '/fill 21 0 3 21 0 3 minecraft:diorite_slab',
 '/fill 22 0 3 23 0 3 minecraft:snow_block',
 '/fill 24 0 3 24 0 3 minecraft:sand',
 '/fill 25 0 3 25 0 3 minecraft:sponge',
 '/fill 26 0 3 26 0 3 minecraft:sand',
 '/fill 27 0 3 29 0 3 minecraft:snow_block',
 '/fill 30 0 3 30 0 3 minecraft:sand',
 '/fill 31 0 3 45 0 3 minecraft:diorite_slab',
 '/fill 0 0 4 13 0 4 minecraft:diorite_slab',
 '/fill 14 0 4 14 0 4 minecraft:block_of_raw_iron',
 '/fill 15 0 4 15 0 4 minecraft:sand',
 '/fill 16 0 4 18 0 4 minecraft:snow_block',
 '/fill 19 0 4 19 0 4 minecraft:sand',
 '/fill 20 0 4 20 0 4 minecraft:sponge',
 '/fill 21 0 4 21 0 4 minecraft:sand',
 '/fill 22 0 4 22 0 4 minecraft:snow_block',
 '/fill 23 0 4 23 0 4 minecraft:diorite_slab',
 '/fill 24 0 4 24 0 4 minecraft:block_of_gold',
 '/fill 25 0 4 25 0 4 minecraft:sponge',
 '/fill 26 0 4 26 0 4 minecraft:white_terracotta',
 '/fill 27 0 4 27 0 4 minecraft:snow_block',
 '/fill 28 0 4 28 0 4 minecraft:diorite_slab',
 '/fill 29 0 4 29 0 4 minecraft:sand',
 '/fill 30 0 4 30 0 4 minecraft:sponge',
 '/fill 31 0 4 31 0 4 minecraft:diorite_slab',
 '/fill 32 0 4 32 0 4 minecraft:snow_block',
 '/fill 33 0 4 45 0 4 minecraft:diorite_slab']
  • Related