Разделить строку на несколько строк, равномерно распределяя определенные значения и сохраняя определенные статические - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть таблица в формате JSON (список диктов), где каждая строка является диктом.

Скажем для простоты, что у меня есть такая строка:

{
    'dimension1': 'foo',
    'dimension2': 'bar',
    'metric1': 102,
    'metric2': 200
}

Я хотел бы знать, если есть простой способ (может быть, используя панды или любой другой инструмент Python), чтобы разбить эту строку на заданное количество n строк, где:

  1. Размеры будут сохранены какis.
  2. Значения метрик будут равномерно распределены по всем строкам.
  3. Все метрики int и должны сохраняться int.
  4. Сумма должна быть равнаисходная строка.

Например, если n = 4, вывод для строки выше должен быть:

[{
    'dimension1': 'foo',
    'dimension2': 'bar',
    'metric1': 25,
    'metric2': 50
},{
    'dimension1': 'foo',
    'dimension2': 'bar',
    'metric1': 25,
    'metric2': 50
},{
    'dimension1': 'foo',
    'dimension2': 'bar',
    'metric1': 26,
    'metric2': 50
},{
    'dimension1': 'foo',
    'dimension2': 'bar',
    'metric1': 26,
    'metric2': 50
}]

Я пытался найти способ сделать это сpandas или другие инструменты, но не удалось найти способ дать набор измерений, которые должны быть статичными, и набор показателей, которые должны быть разделены при сохранении суммы.

Надеюсь, это достаточно ясно,Я знаю, что можно написать эту логику явно, но хотел знать, есть ли какой-нибудь более простой, более надежный способ, которого я здесь упускаю.

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Может быть не самым чистым, но попытайтесь использовать np.histrogram для преобразования значений в бины

def value_to_bins(df_value,n):
    value=np.arange(df_value, dtype=int)
    return np.histogram(value, bins=n)[0]

import pandas as pd
import numpy as np
d={
    'dimension1': 'foo',
    'dimension2': 'bar',
    'metric1': 101,
    'metric2': 200
}
df=pd.DataFrame(d,index=[0])
n=2

df2=pd.DataFrame(index=range(n),columns=['dimension1','dimension2']) # create new dataframe with NaN
df2.dimension1=df2.dimension1.fillna(df.dimension1[0]) # fill with values of previous dimension1
df2.dimension2=df2.dimension2.fillna(df.dimension2[0]) # fill with values of previous dimension2

df2['metric1'] = value_to_bins(df.metric1[0],n)
df2['metric2'] = value_to_bins(df.metric2[0],n)
df2.to_dict('records')

Выходные данные

[{'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 50L, 'metric2': 100L},
 {'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 51L, 'metric2': 100L}]

Всохранить int значения

[{k:int(v) if v!=np.nan and k in ['metric1','metric2']  else v for k,v in i.items() } for i in df2.to_dict('records')]

Выход

[{'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 50, 'metric2': 100},
 {'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 51, 'metric2': 100}]
0 голосов
/ 11 февраля 2019

Вы можете использовать пол и списки и словарь: идея - это пол вычисления, затем разделите и поделите напоминание на 1 для каждого элемента, чтобы иметь максимально близкий элемент, например, предполагая, что 102 и n=4 у нас есть reminder=2, поэтому результат: 25+1,25+1,25,25

import math

data={
'dimension1': 'foo',
'dimension2': 'bar',
'metric1': 102,
'metric2': 203
}
#finds all keys with integer values
division_fields=[k for k,v in data.items() if str(v).isdigit()]
values={}
n=4
#creates a list with desired  values for each numeric field
#and diveds reminder betweens elements of list by 1 foreach element 
for  field in division_fields:
    values[field]= [math.floor(data[field]/n) if i+1>data[field]%n else math.floor(data[field]/n)+1 for i in range(0,n)]

result=[{k:values[k][i] if k in division_fields else v for k,v in data.items() } for i in range(0,n)]

print (result)

Выход (для n = 4):

[{'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 26, 'metric2': 51},
 {'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 26, 'metric2': 51},
 {'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 25, 'metric2': 51},
 {'dimension1': 'foo', 'dimension2': 'bar', 'metric1': 25, 'metric2': 50}]
...