Взвешенная скрипка - PullRequest
       23

Взвешенная скрипка

0 голосов
/ 23 ноября 2018

У меня есть данные Pandas о мощности генератора (МВт) по типу топлива.Я хотел показать предполагаемое распределение мощности завода двумя способами: по заводу (легко) и по МВт (сложнее).Вот пример:

# import libraries
import pandas as pd
import numpy as np
import seaborn as sns

# generate empty dataframe
df = pd.DataFrame(data=None,columns=['Fuel','MW'])

# create and seed a randomstate object (to make #s repeatable below)
rnd = np.random.RandomState(7)

# generate fake data for each fuel type and append to df
for myfuel in ['Biomass','Coal','Hydro','Natural Gas','Oil','Solar','Wind','Other']:
    mymean = rnd.uniform(low=2.8,high=3.2)
    mysigma = rnd.uniform(low=0.6,high=1.0)
    df = df.append(
                   pd.DataFrame({'Fuel': myfuel,
                        'MW': np.array(rnd.lognormal(mean=mymean,sigma=mysigma,size=1000))
                       }),
                   ignore_index=True
                   )

# make violinplot
sns.violinplot(x = 'Fuel',
               y = 'MW',
               data=df,
               inner=None,
               scale='area',
               cut=0,
               linewidth=0.5
              )   

А вот график предполагаемых распределений размера завода по МВт, который составляет этот код:

un-weighted violinplot

Эта скрипка очень обманчива и лишена контекста.Поскольку он не взвешен, тонкий хвост в верхней части каждой категории скрывает тот факт, что относительно немного растений в хвосте содержат много (возможно, даже большую часть) МВт мощности.Поэтому я хочу второй сюжет с распределением по MWs - по сути, это взвешенная версия этого первого скрипичного сюжета.

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

Я подумал, что мог бы пройтись по каждой строке моего кадра данных уровня завода и разложить данные завода (в новый кадр данных) в данные уровня MW.Например, для строки в кадре данных уровня завода, на котором показана установка мощностью 350 МВт, я мог бы разложить ее на 3500 новых строк моего нового кадра данных, каждый из которых представляет мощность 100 кВт.(Я думаю, что мне нужно перейти как минимум к уровню разрешения 100 кВт, потому что некоторые из этих станций довольно малы, в диапазоне 100 кВт.) Этот новый кадр данных будет огромным, но тогда я мог бы сделать скрипку с разложеннымданные.Это казалось немного грубой силой.Есть лучшие идеи для подхода?


Обновление:

Я реализовал метод грубой силы, описанный выше.Вот как это выглядит, если кому-то интересно.Это не «ответ» на этот вопрос, потому что мне все равно было бы интересно, если кто-нибудь знает более элегантный / простой / эффективный способ сделать это.Поэтому, пожалуйста, присоединяйтесь, если вы знаете такой способ.В противном случае, я надеюсь, что этот метод грубой силы мог бы пригодиться кому-то в будущем.

Чтобы было легко понять, что взвешенный план скрипки имеет смысл, я заменил случайные данные на простой однородный ряд чисел от 010. В соответствии с этим новым подходом, участок скрипки df должен выглядеть довольно равномерно, а участок скрипки взвешенных данных (dfw) должен постепенно расширяться к вершине скрипки.Это именно то, что происходит (см. Изображение скрипичных участков ниже).

# import libraries
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# generate empty dataframe
df = pd.DataFrame(data=None,columns=['Fuel','MW'])

# generate fake data for each fuel type and append to df
for myfuel in ['Biomass','Coal','Hydro','Natural Gas','Oil','Solar','Wind','Other']:
    df = df.append(
                   pd.DataFrame({'Fuel': myfuel,
                        # To make it easy to see that the violinplot of dfw (below)
                        # makes sense, here we'll just use a simple range list from
                        # 0 to 10
                        'MW': np.array(range(11))
                       }),
                   ignore_index=True
                   )

# I have to recast the data type here to avoid an error when using violinplot below
df.MW = df.MW.astype(float)

# create another empty dataframe
dfw = pd.DataFrame(data=None,columns=['Fuel','MW'])
# since dfw will be huge, specify data types (in particular, use "category" for Fuel to limit dfw size)
dfw = dfw.astype(dtype={'Fuel':'category', 'MW':'float'})

# Define the MW size by which to normalize all of the units
# Careful: too big -> loss of fidelity in data for small plants
#          too small -> dfw will need to store an enormous amount of data
norm = 0.1 # this is in MW, so 0.1 MW = 100 kW

# Define a var to represent (for each row) how many basic units
# of size = norm there are in each row
mynum = 0

# loop through rows of df
for index, row in df.iterrows():

    # calculate and store the number of norm MW there are within the MW of each plant
    mynum = int(round(row['MW']/norm))

    # insert mynum rows into dfw, each with Fuel = row['Fuel'] and MW = row['MW']
    dfw = dfw.append(
                   pd.DataFrame({'Fuel': row['Fuel'],
                                 'MW': np.array([row['MW']]*mynum,dtype='float')
                                 }),
                                 ignore_index=True
                    )


# Set up figure and axes
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, sharey='row')

# make violinplot
sns.violinplot(x = 'Fuel',
               y = 'MW',
               data=df,
               inner=None,
               scale='area',
               cut=0,
               linewidth=0.5,
               ax = ax1
              )   

# make violinplot
sns.violinplot(x = 'Fuel',
               y = 'MW',
               data=dfw,
               inner=None,
               scale='area',
               cut=0,
               linewidth=0.5,
               ax = ax2
              ) 

# loop through the set of tick labels for both axes
# set tick label size and rotation
for item in (ax1.get_xticklabels() + ax2.get_xticklabels()): 
    item.set_fontsize(8)
    item.set_rotation(30)
    item.set_horizontalalignment('right')

plt.show()

un-weighted and weighted violinplots

...