如何按行对数据框中的元素进行分组

How to group elements in dataframe by row

我有 this code 本质上是将图像的像素转换为 minecraft 中的一个方块,它为您提供了一个“地图”,说明该方块的放置位置,以便在游戏中形成图像:代码生成一个 csv 文件,其中电子表格中的每个单元格对应一个块。

我想改进这一点,不仅输出 csv 文件,还输出一个 txt 文件,其中包含将在游戏中生成图像的 minecraft 命令。

此外,这些命令必须与玩家的 (x, y, z) 有相对位置,这应该通过元组或其他方式在代码中告知。

因此,假设玩家在游戏中位于 0, 0, 0,并且我的 csv 看起来像

这是用于测试的字典形式的相同数据框

{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'}}

我希望我的 txt 输出是

/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

对于那些不知道 minecraft 命令语法的人:

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

[开始,结束](均包括在内),x 横向移动,z 向上或向下移动。

我几乎尝试了所有方法,但我几乎放弃了这段代码,所以我删除了我的进度,抱歉:(

有人可以帮忙吗?提前致谢。

编辑 1.:为了更好地测试,这里是火影忍者的像素艺术(前 5 行)

{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'}}

和(我认为是)前 3 列的预期输出(再次假设玩家的位置 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

我尝试基于 un-pivoting 将值矩阵写入坐标表示来编写解决方案,但压缩代码最终变得非常复杂,所以我放弃了它,并编写了一个基于而是嵌套循环,最终更容易编写。

基本上,这会循环遍历行,并且在每一行中,它将相同 material 的块分组,只要这些块是连续的。 (这就是 itertools.groupby() 的目的,它与 Pandas groupby 不同,因为 Pandas 版本 分组不连续的值一样。)

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)

这是这段代码的输出:

/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

这是一个主要使用 shiftgroupby 的解决方案:

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}'

使用方法:

# 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)

关于您的第一个示例数据:

>>> 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']

在您为“火影忍者的像素艺术”提供的前 5 行中:

>>> 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']

附录: 使脚本相对于当前位置。

import numpy as np


def script(df, at=(0,0,0)):
    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]
            sp = np.array((s['x'], 0, s['z'])) + at
            ep = np.array((e['x'], 0, e['z'])) + at
            line = ' '.join(tuple(sp.astype(str)) + tuple(ep.astype(str)) + (material, ))
            yield f'/fill {line}'

示例:

pos = (-5, 7, 3)
a = list(script(df, at=pos))
b = list(script(df.T, at=pos))
res = min([a, b], key=len)

>>> res
['/fill -5 7 3 -5 7 4 minecraft:diorite',
 '/fill -5 7 5 -5 7 5 minecraft:stone',
 '/fill -4 7 3 -4 7 4 minecraft:stone',
 '/fill -4 7 5 -4 7 5 minecraft:cobblestone',
 '/fill -3 7 3 -3 7 3 minecraft:block_of_raw_iron',
 '/fill -3 7 4 -3 7 4 minecraft:diamond_block',
 '/fill -3 7 5 -3 7 5 minecraft:gold_block']