Add app.py
Browse files- .gitignore +159 -0
- README.md +3 -3
- app.py +106 -0
- example.csv +5 -0
    	
        .gitignore
    ADDED
    
    | @@ -0,0 +1,159 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            # Byte-compiled / optimized / DLL files
         | 
| 2 | 
            +
            __pycache__/
         | 
| 3 | 
            +
            *.py[cod]
         | 
| 4 | 
            +
            *$py.class
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            # C extensions
         | 
| 7 | 
            +
            *.so
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            # Distribution / packaging
         | 
| 10 | 
            +
            .Python
         | 
| 11 | 
            +
            build/
         | 
| 12 | 
            +
            develop-eggs/
         | 
| 13 | 
            +
            dist/
         | 
| 14 | 
            +
            downloads/
         | 
| 15 | 
            +
            eggs/
         | 
| 16 | 
            +
            .eggs/
         | 
| 17 | 
            +
            lib/
         | 
| 18 | 
            +
            lib64/
         | 
| 19 | 
            +
            parts/
         | 
| 20 | 
            +
            sdist/
         | 
| 21 | 
            +
            var/
         | 
| 22 | 
            +
            wheels/
         | 
| 23 | 
            +
            share/python-wheels/
         | 
| 24 | 
            +
            *.egg-info/
         | 
| 25 | 
            +
            .installed.cfg
         | 
| 26 | 
            +
            *.egg
         | 
| 27 | 
            +
            MANIFEST
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            # PyInstaller
         | 
| 30 | 
            +
            #  Usually these files are written by a python script from a template
         | 
| 31 | 
            +
            #  before PyInstaller builds the exe, so as to inject date/other infos into it.
         | 
| 32 | 
            +
            *.manifest
         | 
| 33 | 
            +
            *.spec
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            # Installer logs
         | 
| 36 | 
            +
            pip-log.txt
         | 
| 37 | 
            +
            pip-delete-this-directory.txt
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            # Unit test / coverage reports
         | 
| 40 | 
            +
            htmlcov/
         | 
| 41 | 
            +
            .tox/
         | 
| 42 | 
            +
            .nox/
         | 
| 43 | 
            +
            .coverage
         | 
| 44 | 
            +
            .coverage.*
         | 
| 45 | 
            +
            .cache
         | 
| 46 | 
            +
            nosetests.xml
         | 
| 47 | 
            +
            coverage.xml
         | 
| 48 | 
            +
            *.cover
         | 
| 49 | 
            +
            *.py,cover
         | 
| 50 | 
            +
            .hypothesis/
         | 
| 51 | 
            +
            .pytest_cache/
         | 
| 52 | 
            +
            cover/
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            # Translations
         | 
| 55 | 
            +
            *.mo
         | 
| 56 | 
            +
            *.pot
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            # Django stuff:
         | 
| 59 | 
            +
            *.log
         | 
| 60 | 
            +
            local_settings.py
         | 
| 61 | 
            +
            db.sqlite3
         | 
| 62 | 
            +
            db.sqlite3-journal
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            # Flask stuff:
         | 
| 65 | 
            +
            instance/
         | 
| 66 | 
            +
            .webassets-cache
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            # Scrapy stuff:
         | 
| 69 | 
            +
            .scrapy
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            # Sphinx documentation
         | 
| 72 | 
            +
            docs/_build/
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            # PyBuilder
         | 
| 75 | 
            +
            .pybuilder/
         | 
| 76 | 
            +
            target/
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            # Jupyter Notebook
         | 
| 79 | 
            +
            .ipynb_checkpoints
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            # IPython
         | 
| 82 | 
            +
            profile_default/
         | 
| 83 | 
            +
            ipython_config.py
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            # pyenv
         | 
| 86 | 
            +
            #   For a library or package, you might want to ignore these files since the code is
         | 
| 87 | 
            +
            #   intended to run in multiple environments; otherwise, check them in:
         | 
| 88 | 
            +
            # .python-version
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            # pipenv
         | 
| 91 | 
            +
            #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
         | 
| 92 | 
            +
            #   However, in case of collaboration, if having platform-specific dependencies or dependencies
         | 
| 93 | 
            +
            #   having no cross-platform support, pipenv may install dependencies that don't work, or not
         | 
| 94 | 
            +
            #   install all needed dependencies.
         | 
| 95 | 
            +
            #Pipfile.lock
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            # poetry
         | 
| 98 | 
            +
            #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
         | 
| 99 | 
            +
            #   This is especially recommended for binary packages to ensure reproducibility, and is more
         | 
| 100 | 
            +
            #   commonly ignored for libraries.
         | 
| 101 | 
            +
            #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
         | 
| 102 | 
            +
            #poetry.lock
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            # pdm
         | 
| 105 | 
            +
            #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
         | 
| 106 | 
            +
            #pdm.lock
         | 
| 107 | 
            +
            #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
         | 
| 108 | 
            +
            #   in version control.
         | 
| 109 | 
            +
            #   https://pdm.fming.dev/#use-with-ide
         | 
| 110 | 
            +
            .pdm.toml
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
         | 
| 113 | 
            +
            __pypackages__/
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            # Celery stuff
         | 
| 116 | 
            +
            celerybeat-schedule
         | 
| 117 | 
            +
            celerybeat.pid
         | 
| 118 | 
            +
             | 
| 119 | 
            +
            # SageMath parsed files
         | 
| 120 | 
            +
            *.sage.py
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            # Environments
         | 
| 123 | 
            +
            .env
         | 
| 124 | 
            +
            .venv
         | 
| 125 | 
            +
            env/
         | 
| 126 | 
            +
            venv/
         | 
| 127 | 
            +
            ENV/
         | 
| 128 | 
            +
            env.bak/
         | 
| 129 | 
            +
            venv.bak/
         | 
| 130 | 
            +
             | 
| 131 | 
            +
            # Spyder project settings
         | 
| 132 | 
            +
            .spyderproject
         | 
| 133 | 
            +
            .spyproject
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            # Rope project settings
         | 
| 136 | 
            +
            .ropeproject
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            # mkdocs documentation
         | 
| 139 | 
            +
            /site
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            # mypy
         | 
| 142 | 
            +
            .mypy_cache/
         | 
| 143 | 
            +
            .dmypy.json
         | 
| 144 | 
            +
            dmypy.json
         | 
| 145 | 
            +
             | 
| 146 | 
            +
            # Pyre type checker
         | 
| 147 | 
            +
            .pyre/
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            # pytype static type analyzer
         | 
| 150 | 
            +
            .pytype/
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            # Cython debug symbols
         | 
| 153 | 
            +
            cython_debug/
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            # PyCharm
         | 
| 156 | 
            +
            .idea/
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            # Gradio
         | 
| 159 | 
            +
            flagged/
         | 
    	
        README.md
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 | 
             
            ---
         | 
| 2 | 
             
            title: Ranking
         | 
| 3 | 
            -
            emoji:  | 
| 4 | 
             
            colorFrom: green
         | 
| 5 | 
             
            colorTo: purple
         | 
| 6 | 
             
            sdk: gradio
         | 
| 7 | 
             
            sdk_version: 3.41.2
         | 
| 8 | 
             
            app_file: app.py
         | 
| 9 | 
            -
            pinned:  | 
| 10 | 
             
            license: apache-2.0
         | 
| 11 | 
             
            ---
         | 
| 12 |  | 
| 13 | 
            -
             | 
|  | |
| 1 | 
             
            ---
         | 
| 2 | 
             
            title: Ranking
         | 
| 3 | 
            +
            emoji: 💯
         | 
| 4 | 
             
            colorFrom: green
         | 
| 5 | 
             
            colorTo: purple
         | 
| 6 | 
             
            sdk: gradio
         | 
| 7 | 
             
            sdk_version: 3.41.2
         | 
| 8 | 
             
            app_file: app.py
         | 
| 9 | 
            +
            pinned: true
         | 
| 10 | 
             
            license: apache-2.0
         | 
| 11 | 
             
            ---
         | 
| 12 |  | 
| 13 | 
            +
            It's all about ranking.
         | 
    	
        app.py
    ADDED
    
    | @@ -0,0 +1,106 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import typing
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import gradio as gr
         | 
| 4 | 
            +
            import numpy as np
         | 
| 5 | 
            +
            import numpy.typing as npt
         | 
| 6 | 
            +
            import pandas as pd
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            # https://gist.github.com/dustalov/41678b70c40ba5a55430fa5e77b121d9#file-newman-py
         | 
| 10 | 
            +
            def aggregate(wins: npt.NDArray[np.int64], ties: npt.NDArray[np.int64], tolerance: float = 10e-6, limit: int = 20) \
         | 
| 11 | 
            +
                    -> npt.ArrayLike:
         | 
| 12 | 
            +
                assert wins.shape == ties.shape, 'wins and ties shapes are different'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                pi, v = np.random.rand(wins.shape[0]), np.random.rand()
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                converged, iterations = False, 0
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                while not converged:
         | 
| 19 | 
            +
                    iterations += 1
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    v_numerator = np.sum(
         | 
| 22 | 
            +
                        ties * (pi[:, None] + pi) / (pi[:, None] + pi + 2 * v * np.sqrt(pi[:, None] * pi))
         | 
| 23 | 
            +
                    ) / 2
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    v_denominator = np.sum(
         | 
| 26 | 
            +
                        wins * 2 * np.sqrt(pi[:, None] * pi) / (pi[:, None] + pi + 2 * v * np.sqrt(pi[:, None] * pi))
         | 
| 27 | 
            +
                    )
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    v = v_numerator / v_denominator
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    pi_old = pi.copy()
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    pi_numerator = np.sum(
         | 
| 34 | 
            +
                        (wins + ties / 2) * (pi + v * np.sqrt(pi[:, np.newaxis] * pi)) /
         | 
| 35 | 
            +
                        (pi[:, np.newaxis] + pi + 2 * np.sqrt(pi[:, np.newaxis] * pi)),
         | 
| 36 | 
            +
                        axis=1
         | 
| 37 | 
            +
                    )
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    pi_denominator = np.sum(
         | 
| 40 | 
            +
                        (wins + ties / 2) * (1 + v * np.sqrt(pi[:, np.newaxis] * pi)) /
         | 
| 41 | 
            +
                        (pi[:, np.newaxis] + pi + 2 * np.sqrt(pi[:, np.newaxis] * pi)),
         | 
| 42 | 
            +
                        axis=0
         | 
| 43 | 
            +
                    )
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    pi = pi_numerator / pi_denominator
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    converged = bool(np.all(np.abs(pi / (pi + 1) - pi_old / (pi_old + 1)) < tolerance)) or (iterations >= limit)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                return pi
         | 
| 50 | 
            +
             | 
| 51 | 
            +
             | 
| 52 | 
            +
            def handler(file: typing.IO[bytes]) -> pd.DataFrame:
         | 
| 53 | 
            +
                try:
         | 
| 54 | 
            +
                    df = pd.read_csv(file.name, dtype=str)
         | 
| 55 | 
            +
                except ValueError as e:
         | 
| 56 | 
            +
                    raise gr.Error(f'Parsing error: {e}')
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                if not pd.Series(['left', 'right', 'winner']).isin(df.columns).all():
         | 
| 59 | 
            +
                    raise gr.Error('Columns must exist: left, right, winner')
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                if not df['winner'].isin(pd.Series(['left', 'right', 'tie'])).all():
         | 
| 62 | 
            +
                    raise gr.Error('Allowed winner values: left, right, tie')
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                df = df[['left', 'right', 'winner']]
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                df.dropna(axis='rows', inplace=True)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                index = pd.Index(np.unique(df[['left', 'right']].values), name='item')
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                df_wins = pd.pivot_table(df[df['winner'].isin(['left', 'right'])],
         | 
| 71 | 
            +
                                         index='left', columns='right', values='winner',
         | 
| 72 | 
            +
                                         aggfunc='count', fill_value=0)
         | 
| 73 | 
            +
                df_wins = df_wins.reindex(labels=index, columns=index, fill_value=0)
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                df_ties = pd.pivot_table(df[df['winner'] == 'tie'],
         | 
| 76 | 
            +
                                         index='left', columns='right', values='winner', aggfunc='count',
         | 
| 77 | 
            +
                                         fill_value=0)
         | 
| 78 | 
            +
                df_ties = df_ties.reindex(labels=index, columns=index, fill_value=0)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                wins = df_wins.to_numpy(dtype=np.int64)
         | 
| 81 | 
            +
                ties = df_ties.to_numpy(dtype=np.int64)
         | 
| 82 | 
            +
                ties += ties.T
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                scores = aggregate(wins, ties)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                df_result = pd.DataFrame(data={'score': scores}, index=index)
         | 
| 87 | 
            +
                df_result['rank'] = df_result['score'].rank(ascending=False).astype(int)
         | 
| 88 | 
            +
                df_result.sort_values(by=['rank', 'score'], ascending=[True, False], inplace=True)
         | 
| 89 | 
            +
                df_result.reset_index(inplace=True)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                return df_result
         | 
| 92 | 
            +
             | 
| 93 | 
            +
             | 
| 94 | 
            +
            iface = gr.Interface(
         | 
| 95 | 
            +
                fn=handler,
         | 
| 96 | 
            +
                inputs=gr.File(
         | 
| 97 | 
            +
                    value='example.csv',
         | 
| 98 | 
            +
                    file_types=['.tsv', '.csv']
         | 
| 99 | 
            +
                ),
         | 
| 100 | 
            +
                outputs=gr.Dataframe(
         | 
| 101 | 
            +
                    headers=['item', 'score', 'rank']
         | 
| 102 | 
            +
                ),
         | 
| 103 | 
            +
                allow_flagging='never'
         | 
| 104 | 
            +
            )
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            iface.launch()
         | 
    	
        example.csv
    ADDED
    
    | @@ -0,0 +1,5 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            left,right,winner
         | 
| 2 | 
            +
            a,b,left
         | 
| 3 | 
            +
            b,c,right
         | 
| 4 | 
            +
            c,a,left
         | 
| 5 | 
            +
            a,c,tie
         |