简体   繁体   English

Python在__init__中创建ifif以获取包和函数

[英]Python create if elif in __init__ for package and function

I am combining all defined function into a class and use if , elif to operate. 我将所有已定义的函数组合到一个class并使用ifelif进行操作。
I will explain in the following. 我将在下面解释。

First, I have a 3 types of plot, combo , line , and bar . 首先,我有3种类型的plot, combolinebar
I know how to define function separately for these three plot. 我知道如何为这三个图分别定义函数。

Second, I want to combine these 3 plots together within a package using if . 其次,我想使用if将这3个图组合到一个包中。
The code I tried is: 我试过的代码是:

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


class AP(object):

    def __init__(self, dt, date, group, value, value2, value3, value4, value5, value6, TYPE):
        self.dt = dt
        self.date = date
        self.group= carrier
        self.value = value
        self.col1 = col1
        self.col2 = col2
        self.col3 = col3
        self.col4 = col4
        self.TYPE = TYPE

        if self.TYPE == "combo":

            def ComboChart(self, dt, date, group, value, TYPE):
                dataset = pd.read_csv(dt)
                dataset['date'] = pd.to_datetime(dataset[date])
                dataset['yq'] = pd.PeriodIndex(dataset['date'], freq='Q')
                dataset['qtr'] = dataset['date'].dt.quarter
                dataset = dataset.groupby([carrier, 'yq', 'qtr'])[value].sum().reset_index()
                dataset['total.YQGR'] = dataset[value] / dataset.groupby(['qtr', carrier])[value].transform('shift') - 1
                dataset = dataset[np.isfinite(dataset['total.YQGR'])]
                dataset['total.R'] = dataset[value] / dataset.groupby(group)[value].transform('first')
                dataset.yq = dataset.yq.astype(str)

                fig, ax1 = plt.subplots(figsize=(12,7))
                ax2=ax1.twinx()
                sns.lineplot(x='yq',y='total.R', data=dataset, hue=group, ax=ax1, legend = None, palette = ('navy', 'r'), linewidth=5)
                ax1.set_xticklabels(ax1.get_xticks(), rotation=45, fontsize=15, weight = 'heavy')
                ax1.set_xlabel("", fontsize=15)
                ax1.set_ylabel("")
                ax1.set_ylim((0, max(dataset['total.R']) + 0.05))
                sns.barplot(x='yq', y='total.YQGR', data=dataset, hue=group, ax=ax2, palette = ('navy', 'r'))
                ax2.set_yticklabels(['{:.1f}%'.format(a*100) for a in ax2.get_yticks()])
                ax2.set_ylabel("")
                ax2.set_ylim((min(dataset['total.YQGR']) - 0.01, max(dataset['total.YQGR']) + 0.2))
                ax2.get_legend().remove()
                ax2.legend(bbox_to_anchor=(-0.35, 0.5), loc=2, borderaxespad=0., fontsize = 'xx-large')
                for groups in ax2.containers:
                    for bar in groups:
                        if bar.get_height() >= 0:
                            ax2.text(
                                    bar.get_xy()[0] + bar.get_width()/1.5,
                                    bar.get_height() + 0.003, 
                                '{:.1f}%'.format(round(100*bar.get_height(),2)), 
                                    color='black',
                                    horizontalalignment='center',
                                    fontsize = 12, weight = 'heavy'
                                    )
                        else:
                            ax2.text(
                                    bar.get_xy()[0] + bar.get_width()/1.5,
                                    bar.get_height() - 0.008, 
                                '{:.1f}%'.format(round(100*bar.get_height(),2)), 
                                    color='black',
                                    horizontalalignment='center',
                                    fontsize = 12, weight = 'heavy'
                                    )
                ax1.yaxis.set_visible(False)
                ax2.yaxis.set_visible(False)
                ax2.xaxis.set_visible(False)
                ax1.spines["right"].set_visible(False)
                ax1.spines["left"].set_visible(False)
                ax1.spines["top"].set_visible(False)
                ax1.spines["bottom"].set_visible(False)
                ax2.spines["right"].set_visible(False)
                ax2.spines["left"].set_visible(False)
                ax2.spines["top"].set_visible(False)
                ax2.spines["bottom"].set_visible(False)
                ax1.set_title(TYPE, fontsize=20)
                plt.show()

                fig.savefig(TYPE, bbox_inches='tight', dpi=600)

        elif self.TYPE == "line":

            def line(self, dt, date, carrier, value, value2, TYPE):
                dataset = pd.read_csv(dt)
                dataset['date'] = pd.to_datetime(dataset[date])
                dataset['yq'] = pd.PeriodIndex(dataset['date'], freq='Q')
                dataset = dataset.groupby([group, 'yq'])[value, value2].sum().reset_index()
                dataset['Arate'] = dataset[value2] / dataset[value]
                dataset.yq = dataset.yq.astype(str)

                fig, ax1 = plt.subplots(figsize=(12,7))
                sns.lineplot(x='yq', y='Arate', data=dataset, hue=group, ax=ax1, linewidth=5)
                ax1.set_xticklabels(dataset['yq'], rotation=45, fontsize = 15)
                ax1.set_xlabel("")
                ax1.set_ylabel("")
                ax1.set_ylim((min(dataset['Arate']) - 0.05, max(dataset['Arate']) + 0.05))
                ax1.set_yticklabels(['{:.1f}%'.format(a*100) for a in ax1.get_yticks()], fontsize = 18, weight = 'heavy')
                ax1.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=2, borderaxespad=0., ncol = 6)
                ax1.yaxis.grid(True)
                ax1.spines["right"].set_visible(False)
                ax1.spines["left"].set_visible(False)
                ax1.spines["top"].set_visible(False)
                ax1.spines["bottom"].set_visible(False)
                ax1.set_title(TYPE, fontsize = 20)
                plt.show()

                fig.savefig(TYPE, bbox_inches='tight', dpi=600)

        elif self.TYPE == "bar":

            def Bar(self, dt, date, group, value3, value4, value5, value6, TYPE):
                dataset = pd.read_csv(dt, sep = '|')
                dataset['date'] = pd.to_datetime(dataset[date])
                dataset['yq'] = pd.PeriodIndex(dataset['date'], freq='Q')
                dataset = dataset.groupby([group, 'yq'])[value3, value4, value5, value6].sum().reset_index()
                dataset = dataset.groupby([group]).tail(4)
                dataset.yq = dataset.yq.astype(str)
                dataset = pd.melt(dataset, id_vars = [group, 'yq'], value_vars = [value3, value4, value5, value6])
                dataset = dataset.groupby(['variable', group]).value.sum().reset_index()
                dataset['L4Qtr'] = dataset.value / dataset.groupby([group]).value.transform('sum')

                fig, ax1 = plt.subplots(figsize=(12,7))
                sns.barplot(x='variable', y='L4Qtr', data=dataset, hue=group, ax=ax1)
                ax1.set_xticklabels(ax1.get_xticklabels(), fontsize=17.5, weight = 'heavy')
                ax1.set_xlabel("", fontsize=15)
                ax1.set_ylabel("")
                ax1.yaxis.set_ticks(np.arange(0, max(dataset['L4Qtr']) + 0.1, 0.05), False)
                ax1.set_yticklabels(['{:.1f}%'.format(a*100) for a in ax1.get_yticks()], fontsize = 18, weight = 'heavy')
                ax1.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=2, borderaxespad=0., ncol = 6)
                for groups in ax1.containers:
                    for bar in groups:
                        ax1.text(
                                bar.get_xy()[0] + bar.get_width()/2,
                                bar.get_height() + 0.005, 
                            '{:.1f}%'.format(round(100*bar.get_height(),2)), 
                                color=bar.get_facecolor(),
                                horizontalalignment='center',
                                fontsize = 16, weight = 'heavy'
                                    )
                ax1.spines["right"].set_visible(False)
                ax1.spines["left"].set_visible(False)
                ax1.spines["top"].set_visible(False)
                ax1.spines["bottom"].set_visible(False)
                ax1.set_title(TYPE, fontsize=20)
                plt.show()

                fig.savefig(TYPE, bbox_inches='tight', dpi=600)

Third, I hope others can simply use this module as below: 第三,我希望其他人可以简单地使用此模块,如下所示:

import sys
sys.path.append(r'\\users\desktop\module')
from AP import AP as ap

Finally, when someone assign TYPE , it will automatically plot and save it. 最后,当有人分配TYPE ,它将自动打印并保存它。

# This will plot combo chart
ap(r'\\users\desktop\dataset.csv', date = 'DATEVALUE', group = 'GRPS', value = 'total', TYPE = 'combo')

Above is the ideal thought. 以上是理想的想法。 I do not need to pass value2 ~ value6 in it since combo does not use them. 我不需要在其中传递value2 ~ value6 ,因为combo不使用它们。
When I want bar : 当我想要bar

# This will plot bar chart
ap(r'\\users\desktop\dataset.csv', date = 'DATEVALUE', group = 'GRPS', value3 = 'col1', value4 = 'col2', value5 = 'col3', value6 = 'col4', TYPE = 'combo')

My code is incorrect since error happened. 由于发生错误,我的代码不正确。 It seems that I need to pass all parameters in it. 似乎我需要在其中传递所有参数。

However, even I passed all parameters in it. 但是,即使我在其中传递了所有参数。 No error but no output. 没有错误,但没有输出。

Any suggestion? 有什么建议吗?

could you explain, why you don't just create subclasses for the types? 您能解释一下,为什么不只为类型创建子类吗? Wouldn't that be more straight-forward? 那不是更直接吗?

1.) One way would be to make the subclasses visible to the user and if you don't like this, 1.)一种方法是使子类对用户可见,如果您不喜欢,

2.) you could just create a kind of interface class (eg AP that hides the class that is used behind the scenes and for example instanciates as soon as the type is set. 2.)您可以只创建一种接口类(例如,AP,该类隐藏在幕后使用的类,例如,在设置类型后立即实例化)。

3.) you can work as you began, but then I guess you would have to make the methods visible to the user, because I guess the way you implemented it, the functions are only visible in the init method (maybe your indentaion is not quite correct). 3.)您可以一开始就可以工作,但是我想您必须使这些方法对用户可见,因为我想您实现该方法的方式是,这些函数仅在init方法中可见(也许您的缩进不可见)非常正确)。 For example if your if statements are executed by the init method, then you could assign the methods to instance variables like self.ComboChart= ComboChart to be able to call the method from outside. 例如,如果您的if语句由init方法执行,则可以将这些方法分配给实例变量,例如self.ComboChart = ComboChart,以便能够从外部调用该方法。 But imho that would not be very pythonic and a bit more hacky/less object oriented. 但是,恕我直言,这不是非常pythonic,而是更hacky /更少面向对象。

So I'd suggest 1.) and if that is not possible for some reason, then I'd go for solution 2. Both solutions also allow you to form a clean class structure and reuse code that way, while you are still able to build your simplified interface class if you like. 因此,我建议1.),如果由于某种原因无法实现,那么我将寻求解决方案2。这两种解决方案还允许您形成一个干净的类结构并以这种方式重用代码,同时您仍然可以如果愿意,可以构建简化的接口类。

An example (pseudo code) for method 1 would look like below. 方法1的示例(伪代码)如下所示。 Please note, that I haven't tested it, it is only meant to give you an idea, about splitting logic in an object oriented way. 请注意,我尚未对其进行测试,它仅是为了向您提供有关以面向对象的方式拆分逻辑的想法。 I didn't check your whole solution and so I don't know for example, if you always group your data in the same way. 我没有检查您的整个解决方案,因此例如,您是否始终以相同的方式对数据进行分组,就不知道了。 I'd proabably also separate the presentation logic from the data logic. 我可能还会将表示逻辑与数据逻辑分开。 That would especially be a good idea if you plan to display the same data in more ways, because with the current logic, you would reread the csv file and reporcess the data each time you want another represenatiation. 如果您打算以更多方式显示相同的数据,那将是一个特别好的主意,因为使用当前的逻辑,您每次需要进行另一次重新呈现时,都将重新读取csv文件并重新处理数据。 So not to make it more complicated while I just want to explain the basic principle I ignored this and gave an example for a base class "Chart" and a subclass "ComboChart". 因此,在我只想解释基本原理时,不要使其变得更加复杂,而忽略了这一点,并给出了基类“ Chart”和子类“ ComboChart”的示例。 The "ComboChart" class knows how to read/group the data, because it inherits the methods from "Chart", so you only have to implement it once and thus if you find a bug or want to enhance it later, you only need to do it in one place. “ ComboChart”类知道如何读取/分组数据,因为它继承了“ Chart”的方法,因此您只需实施一次即可,因此,如果您发现错误或以后想对其进行增强,则只需要在一个地方做。 The draw_chart method then only needs to do what's different according to the chosen representation. 然后,draw_chart方法仅需要根据所选择的表示做不同的事情。 A user would have to create the instance of the subclass according the chart type they want to display and call display_chart(). 用户必须根据要显示的图表类型创建子类的实例,然后调用display_chart()。

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


class Chart(object):
    def __init__(self, dt, date, group, value, value2, value3, value4, value5, value6):
        self.dt = dt
        self.date = date
        self.group= carrier
        self.value = value
        self.col1 = col1
        self.col2 = col2
        self.col3 = col3
        self.col4 = col4
        self.TYPE = TYPE
        self.dataset= None

    def _read_data_(self)        
        dataset = pd.read_csv(dt)
        dataset['date'] = pd.to_datetime(dataset[self.date])
        dataset['yq'] = pd.PeriodIndex(dataset['date'], freq='Q')
        dataset['qtr'] = dataset['date'].dt.quarter
        dataset = dataset.groupby([carrier, 'yq', 'qtr'])[value].sum().reset_index()
        dataset['total.YQGR'] = dataset[value] / dataset.groupby(['qtr', carrier])[value].transform('shift') - 1
        dataset = dataset[np.isfinite(dataset['total.YQGR'])]
        dataset['total.R'] = dataset[value] / dataset.groupby(group)[value].transform('first')
        dataset.yq = dataset.yq.astype(str)
        self.dataset= dataset
        return dataset

    def get_data(self):
        if self.dataset is None:
            self._read_data_()
        return self.dataset

    def group_data(self):
        dataset= self.get_data()
        dataset = dataset.groupby([carrier, 'yq', 'qtr'])[value].sum().reset_index()
        dataset['total.YQGR'] = dataset[value] / dataset.groupby(['qtr', carrier])[value].transform('shift') - 1
        dataset = dataset[np.isfinite(dataset['total.YQGR'])]
        dataset['total.R'] = dataset[value] / dataset.groupby(group)[value].transform('first')
        dataset.yq = dataset.yq.astype(str)
        return dataset

    def draw_chart(self):
        pass


class ComboChart(Chart):
    def draw_chart(self):
        dataset = self.group_data()
        fig, ax1 = plt.subplots(figsize=(12,7))
        ax2=ax1.twinx()
        sns.lineplot(x='yq',y='total.R', data=dataset, hue=group, ax=ax1, legend = None, palette = ('navy', 'r'), linewidth=5)
        ax1.set_xticklabels(ax1.get_xticks(), rotation=45, fontsize=15, weight = 'heavy')
        ax1.set_xlabel("", fontsize=15)
        ax1.set_ylabel("")
        ax1.set_ylim((0, max(dataset['total.R']) + 0.05))
        sns.barplot(x='yq', y='total.YQGR', data=dataset, hue=group, ax=ax2, palette = ('navy', 'r'))
        ax2.set_yticklabels(['{:.1f}%'.format(a*100) for a in ax2.get_yticks()])
        ax2.set_ylabel("")
        ax2.set_ylim((min(dataset['total.YQGR']) - 0.01, max(dataset['total.YQGR']) + 0.2))
        ax2.get_legend().remove()
        ax2.legend(bbox_to_anchor=(-0.35, 0.5), loc=2, borderaxespad=0., fontsize = 'xx-large')
        for groups in ax2.containers:
            for bar in groups:
                if bar.get_height() >= 0:
                    ax2.text(
                            bar.get_xy()[0] + bar.get_width()/1.5,
                            bar.get_height() + 0.003, 
                        '{:.1f}%'.format(round(100*bar.get_height(),2)), 
                            color='black',
                            horizontalalignment='center',
                            fontsize = 12, weight = 'heavy'
                            )
                else:
                    ax2.text(
                            bar.get_xy()[0] + bar.get_width()/1.5,
                            bar.get_height() - 0.008, 
                        '{:.1f}%'.format(round(100*bar.get_height(),2)), 
                            color='black',
                            horizontalalignment='center',
                            fontsize = 12, weight = 'heavy'
                            )
        ax1.yaxis.set_visible(False)
        ax2.yaxis.set_visible(False)
        ax2.xaxis.set_visible(False)
        ax1.spines["right"].set_visible(False)
        ax1.spines["left"].set_visible(False)
        ax1.spines["top"].set_visible(False)
        ax1.spines["bottom"].set_visible(False)
        ax2.spines["right"].set_visible(False)
        ax2.spines["left"].set_visible(False)
        ax2.spines["top"].set_visible(False)
        ax2.spines["bottom"].set_visible(False)
        ax1.set_title(TYPE, fontsize=20)
        plt.show()

        fig.savefig(TYPE, bbox_inches='tight', dpi=600)

The second method (with the interface class) would just look the same, only that you have a forth class that is known to the user and knows how to call the real implementation. 第二种方法(带有接口类)看起来是一样的,只是您拥有用户已知的第四类,并且知道如何调用真正的实现。 Like this: 像这样:

class YourInterface:
    def __init__(self, your_arguments, TYPE):
        if TYPE == __ 'ComboChart':
            self.client= ComboChart(your_arguments)
        elif TYPE == ....

    def display_chart(self):
        self.client.display_chart()

But it's a pretty boring class, isnt't it? 但这是一个很无聊的课,不是吗? I'd only do this if your class hierarchy is very technical and could change over time if you want to avoid that the users of your library build up dependencies on the real class hierarchy that would probably be broken as soon as you change your hierarchy. 如果您的类层次结构非常技术性,并且如果您想避免库的用户建立对真实类层次结构的依赖关系,而这种依赖关系可能会在您更改层次结构后立即破坏,那么我只会这样做。 For most cases I guess, class hierarchies stay relatively stable, so you don't need such an extra level of abstraction created by an interface class. 我猜在大多数情况下,类层次结构保持相对稳定,因此您不需要接口类创建的额外抽象级别。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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